add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,258 @@
#
# USB Miscellaneous driver configuration
#
comment "USB Miscellaneous drivers"
depends on USB
config USB_EMI62
tristate "EMI 6|2m USB Audio interface support"
depends on USB
---help---
This driver loads firmware to Emagic EMI 6|2m low latency USB
Audio and Midi interface.
After firmware load the device is handled with standard linux
USB Audio driver.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called audio. If you want to compile it as a
module, say M here and read <file:Documentation/kbuild/modules.txt>.
config USB_EMI26
tristate "EMI 2|6 USB Audio interface support"
depends on USB
---help---
This driver loads firmware to Emagic EMI 2|6 low latency USB
Audio interface.
After firmware load the device is handled with standard linux
USB Audio driver.
To compile this driver as a module, choose M here: the
module will be called emi26.
config USB_ADUTUX
tristate "ADU devices from Ontrak Control Systems"
depends on USB
help
Say Y if you want to use an ADU device from Ontrak Control
Systems.
To compile this driver as a module, choose M here. The module
will be called adutux.
config USB_SEVSEG
tristate "USB 7-Segment LED Display"
depends on USB
help
Say Y here if you have a USB 7-Segment Display by Delcom
To compile this driver as a module, choose M here: the
module will be called usbsevseg.
config USB_RIO500
tristate "USB Diamond Rio500 support"
depends on USB
help
Say Y here if you want to connect a USB Rio500 mp3 player to your
computer's USB port. Please read <file:Documentation/usb/rio.txt>
for more information.
To compile this driver as a module, choose M here: the
module will be called rio500.
config USB_LEGOTOWER
tristate "USB Lego Infrared Tower support"
depends on USB
help
Say Y here if you want to connect a USB Lego Infrared Tower to your
computer's USB port.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called legousbtower. If you want to compile it as
a module, say M here and read
<file:Documentation/kbuild/modules.txt>.
config USB_LCD
tristate "USB LCD driver support"
depends on USB
help
Say Y here if you want to connect an USBLCD to your computer's
USB port. The USBLCD is a small USB interface board for
alphanumeric LCD modules. See <http://www.usblcd.de/> for more
information.
To compile this driver as a module, choose M here: the
module will be called usblcd.
config USB_BERRY_CHARGE
tristate "USB BlackBerry recharge support"
depends on USB
help
Say Y here if you want to connect a BlackBerry device to your
computer's USB port and have it automatically switch to "recharge"
mode.
To compile this driver as a module, choose M here: the
module will be called berry_charge.
config USB_LED
tristate "USB LED driver support"
depends on USB
help
Say Y here if you want to connect an USBLED device to your
computer's USB port.
To compile this driver as a module, choose M here: the
module will be called usbled.
config USB_CYPRESS_CY7C63
tristate "Cypress CY7C63xxx USB driver support"
depends on USB
help
Say Y here if you want to connect a Cypress CY7C63xxx
micro controller to your computer's USB port. Currently this
driver supports the pre-programmed devices (incl. firmware)
by AK Modul-Bus Computer GmbH.
Please see: http://www.ak-modul-bus.de/stat/mikrocontroller.html
To compile this driver as a module, choose M here: the
module will be called cypress_cy7c63.
config USB_CYTHERM
tristate "Cypress USB thermometer driver support"
depends on USB
help
Say Y here if you want to connect a Cypress USB thermometer
device to your computer's USB port. This device is also known
as the Cypress USB Starter kit or demo board. The Elektor
magazine published a modified version of this device in issue
#291.
To compile this driver as a module, choose M here: the
module will be called cytherm.
config USB_IDMOUSE
tristate "Siemens ID USB Mouse Fingerprint sensor support"
depends on USB
help
Say Y here if you want to use the fingerprint sensor on
the Siemens ID Mouse. There is also a Siemens ID Mouse
_Professional_, which has not been tested with this driver,
but uses the same sensor and may therefore work.
This driver creates an entry "/dev/idmouseX" or "/dev/usb/idmouseX",
which can be used by, e.g.,"cat /dev/idmouse0 > fingerprint.pnm".
See also <http://www.fs.tum.de/~echtler/idmouse/>.
config USB_FTDI_ELAN
tristate "Elan PCMCIA CardBus Adapter USB Client"
depends on USB
default M
help
ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters.
Currently only the U132 adapter is available.
The U132 is specifically designed for CardBus PC cards that contain
an OHCI host controller. Typical PC cards are the Orange Mobile 3G
Option GlobeTrotter Fusion card. The U132 adapter will *NOT* work
with PC cards that do not contain an OHCI controller. To use a U132
adapter you will need this "ftdi-elan" module as well as the "u132-hcd"
module which is a USB host controller driver that talks to the OHCI
controller within CardBus card that are inserted in the U132 adapter.
This driver has been tested with a CardBus OHCI USB adapter, and
worked with a USB PEN Drive inserted into the first USB port of
the PCCARD. A rather pointless thing to do, but useful for testing.
See also the USB_U132_HCD entry "Elan U132 Adapter Host Controller"
It is safe to say M here.
config USB_APPLEDISPLAY
tristate "Apple Cinema Display support"
depends on USB
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
help
Say Y here if you want to control the backlight of Apple Cinema
Displays over USB. This driver provides a sysfs interface.
source "drivers/usb/misc/sisusbvga/Kconfig"
config USB_LD
tristate "USB LD driver"
depends on USB
help
This driver is for generic USB devices that use interrupt transfers,
like LD Didactic's USB devices.
To compile this driver as a module, choose M here: the
module will be called ldusb.
config USB_TRANCEVIBRATOR
tristate "PlayStation 2 Trance Vibrator driver support"
depends on USB
help
Say Y here if you want to connect a PlayStation 2 Trance Vibrator
device to your computer's USB port.
To compile this driver as a module, choose M here: the
module will be called trancevibrator.
config USB_IOWARRIOR
tristate "IO Warrior driver support"
depends on USB
help
Say Y here if you want to support the IO Warrior devices from Code
Mercenaries. This includes support for the following devices:
IO Warrior 40
IO Warrior 24
IO Warrior 56
IO Warrior 24 Power Vampire
To compile this driver as a module, choose M here: the
module will be called iowarrior.
config USB_TEST
tristate "USB testing driver"
depends on USB
help
This driver is for testing host controller software. It is used
with specialized device firmware for regression and stress testing,
to help prevent problems from cropping up with "real" drivers.
See <http://www.linux-usb.org/usbtest/> for more information,
including sample test device firmware and "how to use it".
config USB_ISIGHTFW
tristate "iSight firmware loading support"
depends on USB
select FW_LOADER
help
This driver loads firmware for USB Apple iSight cameras, allowing
them to be driven by the USB video class driver available at
http://linux-uvc.berlios.de
The firmware for this driver must be extracted from the MacOS
driver beforehand. Tools for doing so are available at
http://bersace03.free.fr
config USB_VST
tristate "USB VST driver"
depends on USB
help
This driver is intended for Vernier Software Technologies
bulk usb devices such as their Ocean-Optics spectrometers or
Labquest.
It is a bulk channel driver with configurable read and write
timeouts.
To compile this driver as a module, choose M here: the
module will be called vstusb.

View File

@@ -0,0 +1,32 @@
#
# Makefile for the rest of the USB drivers
# (the ones that don't fit into any other categories)
#
obj-$(CONFIG_USB_ADUTUX) += adutux.o
obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o
obj-$(CONFIG_USB_BERRY_CHARGE) += berry_charge.o
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o
obj-$(CONFIG_USB_CYTHERM) += cytherm.o
obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_EMI62) += emi62.o
obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o
obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LD) += ldusb.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o
obj-$(CONFIG_USB_VST) += vstusb.o
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
ifeq ($(CONFIG_USB_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif

View File

@@ -0,0 +1,924 @@
/*
* adutux - driver for ADU devices from Ontrak Control Systems
* This is an experimental driver. Use at your own risk.
* This driver is not supported by Ontrak Control Systems.
*
* Copyright (c) 2003 John Homppi (SCO, leave this notice here)
*
* 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.
*
* derived from the Lego USB Tower driver 0.56:
* Copyright (c) 2003 David Glance <davidgsf@sourceforge.net>
* 2001 Juergen Stuber <stuber@loria.fr>
* that was derived from USB Skeleton driver - 0.5
* Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#ifdef CONFIG_USB_DEBUG
static int debug = 5;
#else
static int debug = 1;
#endif
/* Use our own dbg macro */
#undef dbg
#define dbg(lvl, format, arg...) \
do { \
if (debug >= lvl) \
printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); \
} while (0)
/* Version Information */
#define DRIVER_VERSION "v0.0.13"
#define DRIVER_AUTHOR "John Homppi"
#define DRIVER_DESC "adutux (see www.ontrak.net)"
/* Module parameters */
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
/* Define these values to match your device */
#define ADU_VENDOR_ID 0x0a07
#define ADU_PRODUCT_ID 0x0064
/* table of devices that work with this driver */
static struct usb_device_id device_table [] = {
{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID) }, /* ADU100 */
{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+20) }, /* ADU120 */
{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+30) }, /* ADU130 */
{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+100) }, /* ADU200 */
{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+108) }, /* ADU208 */
{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+118) }, /* ADU218 */
{ }/* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, device_table);
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define ADU_MINOR_BASE 0
#else
#define ADU_MINOR_BASE 67
#endif
/* we can have up to this number of device plugged in at once */
#define MAX_DEVICES 16
#define COMMAND_TIMEOUT (2*HZ) /* 60 second timeout for a command */
/*
* The locking scheme is a vanilla 3-lock:
* adu_device.buflock: A spinlock, covers what IRQs touch.
* adutux_mutex: A Static lock to cover open_count. It would also cover
* any globals, but we don't have them in 2.6.
* adu_device.mtx: A mutex to hold across sleepers like copy_from_user.
* It covers all of adu_device, except the open_count
* and what .buflock covers.
*/
/* Structure to hold all of our device specific stuff */
struct adu_device {
struct mutex mtx;
struct usb_device* udev; /* save off the usb device pointer */
struct usb_interface* interface;
unsigned int minor; /* the starting minor number for this device */
char serial_number[8];
int open_count; /* number of times this port has been opened */
char* read_buffer_primary;
int read_buffer_length;
char* read_buffer_secondary;
int secondary_head;
int secondary_tail;
spinlock_t buflock;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
char* interrupt_in_buffer;
struct usb_endpoint_descriptor* interrupt_in_endpoint;
struct urb* interrupt_in_urb;
int read_urb_finished;
char* interrupt_out_buffer;
struct usb_endpoint_descriptor* interrupt_out_endpoint;
struct urb* interrupt_out_urb;
int out_urb_finished;
};
static DEFINE_MUTEX(adutux_mutex);
static struct usb_driver adu_driver;
static void adu_debug_data(int level, const char *function, int size,
const unsigned char *data)
{
int i;
if (debug < level)
return;
printk(KERN_DEBUG __FILE__": %s - length = %d, data = ",
function, size);
for (i = 0; i < size; ++i)
printk("%.2x ", data[i]);
printk("\n");
}
/**
* adu_abort_transfers
* aborts transfers and frees associated data structures
*/
static void adu_abort_transfers(struct adu_device *dev)
{
unsigned long flags;
dbg(2," %s : enter", __func__);
if (dev->udev == NULL) {
dbg(1," %s : udev is null", __func__);
goto exit;
}
/* shutdown transfer */
/* XXX Anchor these instead */
spin_lock_irqsave(&dev->buflock, flags);
if (!dev->read_urb_finished) {
spin_unlock_irqrestore(&dev->buflock, flags);
usb_kill_urb(dev->interrupt_in_urb);
} else
spin_unlock_irqrestore(&dev->buflock, flags);
spin_lock_irqsave(&dev->buflock, flags);
if (!dev->out_urb_finished) {
spin_unlock_irqrestore(&dev->buflock, flags);
usb_kill_urb(dev->interrupt_out_urb);
} else
spin_unlock_irqrestore(&dev->buflock, flags);
exit:
dbg(2," %s : leave", __func__);
}
static void adu_delete(struct adu_device *dev)
{
dbg(2, "%s enter", __func__);
/* free data structures */
usb_free_urb(dev->interrupt_in_urb);
usb_free_urb(dev->interrupt_out_urb);
kfree(dev->read_buffer_primary);
kfree(dev->read_buffer_secondary);
kfree(dev->interrupt_in_buffer);
kfree(dev->interrupt_out_buffer);
kfree(dev);
dbg(2, "%s : leave", __func__);
}
static void adu_interrupt_in_callback(struct urb *urb)
{
struct adu_device *dev = urb->context;
int status = urb->status;
dbg(4," %s : enter, status %d", __func__, status);
adu_debug_data(5, __func__, urb->actual_length,
urb->transfer_buffer);
spin_lock(&dev->buflock);
if (status != 0) {
if ((status != -ENOENT) && (status != -ECONNRESET) &&
(status != -ESHUTDOWN)) {
dbg(1," %s : nonzero status received: %d",
__func__, status);
}
goto exit;
}
if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) {
if (dev->read_buffer_length <
(4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) -
(urb->actual_length)) {
memcpy (dev->read_buffer_primary +
dev->read_buffer_length,
dev->interrupt_in_buffer, urb->actual_length);
dev->read_buffer_length += urb->actual_length;
dbg(2," %s reading %d ", __func__,
urb->actual_length);
} else {
dbg(1," %s : read_buffer overflow", __func__);
}
}
exit:
dev->read_urb_finished = 1;
spin_unlock(&dev->buflock);
/* always wake up so we recover from errors */
wake_up_interruptible(&dev->read_wait);
adu_debug_data(5, __func__, urb->actual_length,
urb->transfer_buffer);
dbg(4," %s : leave, status %d", __func__, status);
}
static void adu_interrupt_out_callback(struct urb *urb)
{
struct adu_device *dev = urb->context;
int status = urb->status;
dbg(4," %s : enter, status %d", __func__, status);
adu_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
if (status != 0) {
if ((status != -ENOENT) &&
(status != -ECONNRESET)) {
dbg(1, " %s :nonzero status received: %d",
__func__, status);
}
goto exit;
}
spin_lock(&dev->buflock);
dev->out_urb_finished = 1;
wake_up(&dev->write_wait);
spin_unlock(&dev->buflock);
exit:
adu_debug_data(5, __func__, urb->actual_length,
urb->transfer_buffer);
dbg(4," %s : leave, status %d", __func__, status);
}
static int adu_open(struct inode *inode, struct file *file)
{
struct adu_device *dev = NULL;
struct usb_interface *interface;
int subminor;
int retval;
dbg(2,"%s : enter", __func__);
subminor = iminor(inode);
if ((retval = mutex_lock_interruptible(&adutux_mutex))) {
dbg(2, "%s : mutex lock failed", __func__);
goto exit_no_lock;
}
interface = usb_find_interface(&adu_driver, subminor);
if (!interface) {
printk(KERN_ERR "adutux: %s - error, can't find device for "
"minor %d\n", __func__, subminor);
retval = -ENODEV;
goto exit_no_device;
}
dev = usb_get_intfdata(interface);
if (!dev || !dev->udev) {
retval = -ENODEV;
goto exit_no_device;
}
/* check that nobody else is using the device */
if (dev->open_count) {
retval = -EBUSY;
goto exit_no_device;
}
++dev->open_count;
dbg(2,"%s : open count %d", __func__, dev->open_count);
/* save device in the file's private structure */
file->private_data = dev;
/* initialize in direction */
dev->read_buffer_length = 0;
/* fixup first read by having urb waiting for it */
usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
usb_rcvintpipe(dev->udev,
dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
adu_interrupt_in_callback, dev,
dev->interrupt_in_endpoint->bInterval);
dev->read_urb_finished = 0;
if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL))
dev->read_urb_finished = 1;
/* we ignore failure */
/* end of fixup for first read */
/* initialize out direction */
dev->out_urb_finished = 1;
retval = 0;
exit_no_device:
mutex_unlock(&adutux_mutex);
exit_no_lock:
dbg(2,"%s : leave, return value %d ", __func__, retval);
return retval;
}
static void adu_release_internal(struct adu_device *dev)
{
dbg(2," %s : enter", __func__);
/* decrement our usage count for the device */
--dev->open_count;
dbg(2," %s : open count %d", __func__, dev->open_count);
if (dev->open_count <= 0) {
adu_abort_transfers(dev);
dev->open_count = 0;
}
dbg(2," %s : leave", __func__);
}
static int adu_release(struct inode *inode, struct file *file)
{
struct adu_device *dev;
int retval = 0;
dbg(2," %s : enter", __func__);
if (file == NULL) {
dbg(1," %s : file is NULL", __func__);
retval = -ENODEV;
goto exit;
}
dev = file->private_data;
if (dev == NULL) {
dbg(1," %s : object is NULL", __func__);
retval = -ENODEV;
goto exit;
}
mutex_lock(&adutux_mutex); /* not interruptible */
if (dev->open_count <= 0) {
dbg(1," %s : device not opened", __func__);
retval = -ENODEV;
goto unlock;
}
adu_release_internal(dev);
if (dev->udev == NULL) {
/* the device was unplugged before the file was released */
if (!dev->open_count) /* ... and we're the last user */
adu_delete(dev);
}
unlock:
mutex_unlock(&adutux_mutex);
exit:
dbg(2," %s : leave, return value %d", __func__, retval);
return retval;
}
static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
loff_t *ppos)
{
struct adu_device *dev;
size_t bytes_read = 0;
size_t bytes_to_read = count;
int i;
int retval = 0;
int timeout = 0;
int should_submit = 0;
unsigned long flags;
DECLARE_WAITQUEUE(wait, current);
dbg(2," %s : enter, count = %Zd, file=%p", __func__, count, file);
dev = file->private_data;
dbg(2," %s : dev=%p", __func__, dev);
if (mutex_lock_interruptible(&dev->mtx))
return -ERESTARTSYS;
/* verify that the device wasn't unplugged */
if (dev->udev == NULL) {
retval = -ENODEV;
printk(KERN_ERR "adutux: No device or device unplugged %d\n",
retval);
goto exit;
}
/* verify that some data was requested */
if (count == 0) {
dbg(1," %s : read request of 0 bytes", __func__);
goto exit;
}
timeout = COMMAND_TIMEOUT;
dbg(2," %s : about to start looping", __func__);
while (bytes_to_read) {
int data_in_secondary = dev->secondary_tail - dev->secondary_head;
dbg(2," %s : while, data_in_secondary=%d, status=%d",
__func__, data_in_secondary,
dev->interrupt_in_urb->status);
if (data_in_secondary) {
/* drain secondary buffer */
int amount = bytes_to_read < data_in_secondary ? bytes_to_read : data_in_secondary;
i = copy_to_user(buffer, dev->read_buffer_secondary+dev->secondary_head, amount);
if (i < 0) {
retval = -EFAULT;
goto exit;
}
dev->secondary_head += (amount - i);
bytes_read += (amount - i);
bytes_to_read -= (amount - i);
if (i) {
retval = bytes_read ? bytes_read : -EFAULT;
goto exit;
}
} else {
/* we check the primary buffer */
spin_lock_irqsave (&dev->buflock, flags);
if (dev->read_buffer_length) {
/* we secure access to the primary */
char *tmp;
dbg(2," %s : swap, read_buffer_length = %d",
__func__, dev->read_buffer_length);
tmp = dev->read_buffer_secondary;
dev->read_buffer_secondary = dev->read_buffer_primary;
dev->read_buffer_primary = tmp;
dev->secondary_head = 0;
dev->secondary_tail = dev->read_buffer_length;
dev->read_buffer_length = 0;
spin_unlock_irqrestore(&dev->buflock, flags);
/* we have a free buffer so use it */
should_submit = 1;
} else {
/* even the primary was empty - we may need to do IO */
if (!dev->read_urb_finished) {
/* somebody is doing IO */
spin_unlock_irqrestore(&dev->buflock, flags);
dbg(2," %s : submitted already", __func__);
} else {
/* we must initiate input */
dbg(2," %s : initiate input", __func__);
dev->read_urb_finished = 0;
spin_unlock_irqrestore(&dev->buflock, flags);
usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
usb_rcvintpipe(dev->udev,
dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
adu_interrupt_in_callback,
dev,
dev->interrupt_in_endpoint->bInterval);
retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
if (retval) {
dev->read_urb_finished = 1;
if (retval == -ENOMEM) {
retval = bytes_read ? bytes_read : -ENOMEM;
}
dbg(2," %s : submit failed", __func__);
goto exit;
}
}
/* we wait for I/O to complete */
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&dev->read_wait, &wait);
spin_lock_irqsave(&dev->buflock, flags);
if (!dev->read_urb_finished) {
spin_unlock_irqrestore(&dev->buflock, flags);
timeout = schedule_timeout(COMMAND_TIMEOUT);
} else {
spin_unlock_irqrestore(&dev->buflock, flags);
set_current_state(TASK_RUNNING);
}
remove_wait_queue(&dev->read_wait, &wait);
if (timeout <= 0) {
dbg(2," %s : timeout", __func__);
retval = bytes_read ? bytes_read : -ETIMEDOUT;
goto exit;
}
if (signal_pending(current)) {
dbg(2," %s : signal pending", __func__);
retval = bytes_read ? bytes_read : -EINTR;
goto exit;
}
}
}
}
retval = bytes_read;
/* if the primary buffer is empty then use it */
spin_lock_irqsave(&dev->buflock, flags);
if (should_submit && dev->read_urb_finished) {
dev->read_urb_finished = 0;
spin_unlock_irqrestore(&dev->buflock, flags);
usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
usb_rcvintpipe(dev->udev,
dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
adu_interrupt_in_callback,
dev,
dev->interrupt_in_endpoint->bInterval);
if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL) != 0)
dev->read_urb_finished = 1;
/* we ignore failure */
} else {
spin_unlock_irqrestore(&dev->buflock, flags);
}
exit:
/* unlock the device */
mutex_unlock(&dev->mtx);
dbg(2," %s : leave, return value %d", __func__, retval);
return retval;
}
static ssize_t adu_write(struct file *file, const __user char *buffer,
size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(waita, current);
struct adu_device *dev;
size_t bytes_written = 0;
size_t bytes_to_write;
size_t buffer_size;
unsigned long flags;
int retval;
dbg(2," %s : enter, count = %Zd", __func__, count);
dev = file->private_data;
retval = mutex_lock_interruptible(&dev->mtx);
if (retval)
goto exit_nolock;
/* verify that the device wasn't unplugged */
if (dev->udev == NULL) {
retval = -ENODEV;
printk(KERN_ERR "adutux: No device or device unplugged %d\n",
retval);
goto exit;
}
/* verify that we actually have some data to write */
if (count == 0) {
dbg(1," %s : write request of 0 bytes", __func__);
goto exit;
}
while (count > 0) {
add_wait_queue(&dev->write_wait, &waita);
set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&dev->buflock, flags);
if (!dev->out_urb_finished) {
spin_unlock_irqrestore(&dev->buflock, flags);
mutex_unlock(&dev->mtx);
if (signal_pending(current)) {
dbg(1," %s : interrupted", __func__);
set_current_state(TASK_RUNNING);
retval = -EINTR;
goto exit_onqueue;
}
if (schedule_timeout(COMMAND_TIMEOUT) == 0) {
dbg(1, "%s - command timed out.", __func__);
retval = -ETIMEDOUT;
goto exit_onqueue;
}
remove_wait_queue(&dev->write_wait, &waita);
retval = mutex_lock_interruptible(&dev->mtx);
if (retval) {
retval = bytes_written ? bytes_written : retval;
goto exit_nolock;
}
dbg(4," %s : in progress, count = %Zd", __func__, count);
} else {
spin_unlock_irqrestore(&dev->buflock, flags);
set_current_state(TASK_RUNNING);
remove_wait_queue(&dev->write_wait, &waita);
dbg(4," %s : sending, count = %Zd", __func__, count);
/* write the data into interrupt_out_buffer from userspace */
buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
bytes_to_write = count > buffer_size ? buffer_size : count;
dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd",
__func__, buffer_size, count, bytes_to_write);
if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
retval = -EFAULT;
goto exit;
}
/* send off the urb */
usb_fill_int_urb(
dev->interrupt_out_urb,
dev->udev,
usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
dev->interrupt_out_buffer,
bytes_to_write,
adu_interrupt_out_callback,
dev,
dev->interrupt_out_endpoint->bInterval);
dev->interrupt_out_urb->actual_length = bytes_to_write;
dev->out_urb_finished = 0;
retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
if (retval < 0) {
dev->out_urb_finished = 1;
dev_err(&dev->udev->dev, "Couldn't submit "
"interrupt_out_urb %d\n", retval);
goto exit;
}
buffer += bytes_to_write;
count -= bytes_to_write;
bytes_written += bytes_to_write;
}
}
mutex_unlock(&dev->mtx);
return bytes_written;
exit:
mutex_unlock(&dev->mtx);
exit_nolock:
dbg(2," %s : leave, return value %d", __func__, retval);
return retval;
exit_onqueue:
remove_wait_queue(&dev->write_wait, &waita);
return retval;
}
/* file operations needed when we register this driver */
static const struct file_operations adu_fops = {
.owner = THIS_MODULE,
.read = adu_read,
.write = adu_write,
.open = adu_open,
.release = adu_release,
};
/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with devfs and the driver core
*/
static struct usb_class_driver adu_class = {
.name = "usb/adutux%d",
.fops = &adu_fops,
.minor_base = ADU_MINOR_BASE,
};
/**
* adu_probe
*
* Called by the usb core when a new device is connected that it thinks
* this driver might be interested in.
*/
static int adu_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct adu_device *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int retval = -ENODEV;
int in_end_size;
int out_end_size;
int i;
dbg(2," %s : enter", __func__);
if (udev == NULL) {
dev_err(&interface->dev, "udev is NULL.\n");
goto exit;
}
/* allocate memory for our device state and intialize it */
dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
retval = -ENOMEM;
goto exit;
}
mutex_init(&dev->mtx);
spin_lock_init(&dev->buflock);
dev->udev = udev;
init_waitqueue_head(&dev->read_wait);
init_waitqueue_head(&dev->write_wait);
iface_desc = &interface->altsetting[0];
/* set up the endpoint information */
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (usb_endpoint_is_int_in(endpoint))
dev->interrupt_in_endpoint = endpoint;
if (usb_endpoint_is_int_out(endpoint))
dev->interrupt_out_endpoint = endpoint;
}
if (dev->interrupt_in_endpoint == NULL) {
dev_err(&interface->dev, "interrupt in endpoint not found\n");
goto error;
}
if (dev->interrupt_out_endpoint == NULL) {
dev_err(&interface->dev, "interrupt out endpoint not found\n");
goto error;
}
in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);
out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL);
if (!dev->read_buffer_primary) {
dev_err(&interface->dev, "Couldn't allocate read_buffer_primary\n");
retval = -ENOMEM;
goto error;
}
/* debug code prime the buffer */
memset(dev->read_buffer_primary, 'a', in_end_size);
memset(dev->read_buffer_primary + in_end_size, 'b', in_end_size);
memset(dev->read_buffer_primary + (2 * in_end_size), 'c', in_end_size);
memset(dev->read_buffer_primary + (3 * in_end_size), 'd', in_end_size);
dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL);
if (!dev->read_buffer_secondary) {
dev_err(&interface->dev, "Couldn't allocate read_buffer_secondary\n");
retval = -ENOMEM;
goto error;
}
/* debug code prime the buffer */
memset(dev->read_buffer_secondary, 'e', in_end_size);
memset(dev->read_buffer_secondary + in_end_size, 'f', in_end_size);
memset(dev->read_buffer_secondary + (2 * in_end_size), 'g', in_end_size);
memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size);
dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL);
if (!dev->interrupt_in_buffer) {
dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n");
goto error;
}
/* debug code prime the buffer */
memset(dev->interrupt_in_buffer, 'i', in_end_size);
dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->interrupt_in_urb) {
dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
goto error;
}
dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL);
if (!dev->interrupt_out_buffer) {
dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n");
goto error;
}
dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->interrupt_out_urb) {
dev_err(&interface->dev, "Couldn't allocate interrupt_out_urb\n");
goto error;
}
if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number,
sizeof(dev->serial_number))) {
dev_err(&interface->dev, "Could not retrieve serial number\n");
goto error;
}
dbg(2," %s : serial_number=%s", __func__, dev->serial_number);
/* we can register the device now, as it is ready */
usb_set_intfdata(interface, dev);
retval = usb_register_dev(interface, &adu_class);
if (retval) {
/* something prevented us from registering this driver */
dev_err(&interface->dev, "Not able to get a minor for this device.\n");
usb_set_intfdata(interface, NULL);
goto error;
}
dev->minor = interface->minor;
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d\n",
udev->descriptor.idProduct, dev->serial_number,
(dev->minor - ADU_MINOR_BASE));
exit:
dbg(2," %s : leave, return value %p (dev)", __func__, dev);
return retval;
error:
adu_delete(dev);
return retval;
}
/**
* adu_disconnect
*
* Called by the usb core when the device is removed from the system.
*/
static void adu_disconnect(struct usb_interface *interface)
{
struct adu_device *dev;
int minor;
dbg(2," %s : enter", __func__);
dev = usb_get_intfdata(interface);
mutex_lock(&dev->mtx); /* not interruptible */
dev->udev = NULL; /* poison */
minor = dev->minor;
usb_deregister_dev(interface, &adu_class);
mutex_unlock(&dev->mtx);
mutex_lock(&adutux_mutex);
usb_set_intfdata(interface, NULL);
/* if the device is not opened, then we clean up right now */
dbg(2," %s : open count %d", __func__, dev->open_count);
if (!dev->open_count)
adu_delete(dev);
mutex_unlock(&adutux_mutex);
dev_info(&interface->dev, "ADU device adutux%d now disconnected\n",
(minor - ADU_MINOR_BASE));
dbg(2," %s : leave", __func__);
}
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver adu_driver = {
.name = "adutux",
.probe = adu_probe,
.disconnect = adu_disconnect,
.id_table = device_table,
};
static int __init adu_init(void)
{
int result;
dbg(2," %s : enter", __func__);
/* register this driver with the USB subsystem */
result = usb_register(&adu_driver);
if (result < 0) {
printk(KERN_ERR "usb_register failed for the "__FILE__
" driver. Error number %d\n", result);
goto exit;
}
printk(KERN_INFO "adutux " DRIVER_DESC " " DRIVER_VERSION "\n");
printk(KERN_INFO "adutux is an experimental driver. "
"Use at your own risk\n");
exit:
dbg(2," %s : leave, return value %d", __func__, result);
return result;
}
static void __exit adu_exit(void)
{
dbg(2," %s : enter", __func__);
/* deregister this driver with the USB subsystem */
usb_deregister(&adu_driver);
dbg(2," %s : leave", __func__);
}
module_init(adu_init);
module_exit(adu_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,376 @@
/*
* Apple Cinema Display driver
*
* Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
*
* Thanks to Caskey L. Dickson for his work with acdctl.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/backlight.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/atomic.h>
#define APPLE_VENDOR_ID 0x05AC
#define USB_REQ_GET_REPORT 0x01
#define USB_REQ_SET_REPORT 0x09
#define ACD_USB_TIMEOUT 250
#define ACD_USB_EDID 0x0302
#define ACD_USB_BRIGHTNESS 0x0310
#define ACD_BTN_NONE 0
#define ACD_BTN_BRIGHT_UP 3
#define ACD_BTN_BRIGHT_DOWN 4
#define ACD_URB_BUFFER_LEN 2
#define ACD_MSG_BUFFER_LEN 2
#define APPLEDISPLAY_DEVICE(prod) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
.idVendor = APPLE_VENDOR_ID, \
.idProduct = (prod), \
.bInterfaceClass = USB_CLASS_HID, \
.bInterfaceProtocol = 0x00
/* table of devices that work with this driver */
static struct usb_device_id appledisplay_table [] = {
{ APPLEDISPLAY_DEVICE(0x9218) },
{ APPLEDISPLAY_DEVICE(0x9219) },
{ APPLEDISPLAY_DEVICE(0x921d) },
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE(usb, appledisplay_table);
/* Structure to hold all of our device specific stuff */
struct appledisplay {
struct usb_device *udev; /* usb device */
struct urb *urb; /* usb request block */
struct backlight_device *bd; /* backlight device */
u8 *urbdata; /* interrupt URB data buffer */
u8 *msgdata; /* control message data buffer */
struct delayed_work work;
int button_pressed;
spinlock_t lock;
};
static atomic_t count_displays = ATOMIC_INIT(0);
static struct workqueue_struct *wq;
static void appledisplay_complete(struct urb *urb)
{
struct appledisplay *pdata = urb->context;
unsigned long flags;
int status = urb->status;
int retval;
switch (status) {
case 0:
/* success */
break;
case -EOVERFLOW:
printk(KERN_ERR "appletouch: OVERFLOW with data "
"length %d, actual length is %d\n",
ACD_URB_BUFFER_LEN, pdata->urb->actual_length);
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* This urb is terminated, clean up */
dbg("%s - urb shuttingdown with status: %d",
__func__, status);
return;
default:
dbg("%s - nonzero urb status received: %d",
__func__, status);
goto exit;
}
spin_lock_irqsave(&pdata->lock, flags);
switch(pdata->urbdata[1]) {
case ACD_BTN_BRIGHT_UP:
case ACD_BTN_BRIGHT_DOWN:
pdata->button_pressed = 1;
queue_delayed_work(wq, &pdata->work, 0);
break;
case ACD_BTN_NONE:
default:
pdata->button_pressed = 0;
break;
}
spin_unlock_irqrestore(&pdata->lock, flags);
exit:
retval = usb_submit_urb(pdata->urb, GFP_ATOMIC);
if (retval) {
dev_err(&pdata->udev->dev,
"%s - usb_submit_urb failed with result %d\n",
__func__, retval);
}
}
static int appledisplay_bl_update_status(struct backlight_device *bd)
{
struct appledisplay *pdata = bl_get_data(bd);
int retval;
pdata->msgdata[0] = 0x10;
pdata->msgdata[1] = bd->props.brightness;
retval = usb_control_msg(
pdata->udev,
usb_sndctrlpipe(pdata->udev, 0),
USB_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ACD_USB_BRIGHTNESS,
0,
pdata->msgdata, 2,
ACD_USB_TIMEOUT);
return retval;
}
static int appledisplay_bl_get_brightness(struct backlight_device *bd)
{
struct appledisplay *pdata = bl_get_data(bd);
int retval;
retval = usb_control_msg(
pdata->udev,
usb_rcvctrlpipe(pdata->udev, 0),
USB_REQ_GET_REPORT,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ACD_USB_BRIGHTNESS,
0,
pdata->msgdata, 2,
ACD_USB_TIMEOUT);
if (retval < 0)
return retval;
else
return pdata->msgdata[1];
}
static struct backlight_ops appledisplay_bl_data = {
.get_brightness = appledisplay_bl_get_brightness,
.update_status = appledisplay_bl_update_status,
};
static void appledisplay_work(struct work_struct *work)
{
struct appledisplay *pdata =
container_of(work, struct appledisplay, work.work);
int retval;
retval = appledisplay_bl_get_brightness(pdata->bd);
if (retval >= 0)
pdata->bd->props.brightness = retval;
/* Poll again in about 125ms if there's still a button pressed */
if (pdata->button_pressed)
schedule_delayed_work(&pdata->work, HZ / 8);
}
static int appledisplay_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct appledisplay *pdata;
struct usb_device *udev = interface_to_usbdev(iface);
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int int_in_endpointAddr = 0;
int i, retval = -ENOMEM, brightness;
char bl_name[20];
/* set up the endpoint information */
/* use only the first interrupt-in endpoint */
iface_desc = iface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
/* we found an interrupt in endpoint */
int_in_endpointAddr = endpoint->bEndpointAddress;
break;
}
}
if (!int_in_endpointAddr) {
dev_err(&iface->dev, "Could not find int-in endpoint\n");
return -EIO;
}
/* allocate memory for our device state and initialize it */
pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);
if (!pdata) {
retval = -ENOMEM;
dev_err(&iface->dev, "Out of memory\n");
goto error;
}
pdata->udev = udev;
spin_lock_init(&pdata->lock);
INIT_DELAYED_WORK(&pdata->work, appledisplay_work);
/* Allocate buffer for control messages */
pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL);
if (!pdata->msgdata) {
retval = -ENOMEM;
dev_err(&iface->dev,
"Allocating buffer for control messages failed\n");
goto error;
}
/* Allocate interrupt URB */
pdata->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!pdata->urb) {
retval = -ENOMEM;
dev_err(&iface->dev, "Allocating URB failed\n");
goto error;
}
/* Allocate buffer for interrupt data */
pdata->urbdata = usb_buffer_alloc(pdata->udev, ACD_URB_BUFFER_LEN,
GFP_KERNEL, &pdata->urb->transfer_dma);
if (!pdata->urbdata) {
retval = -ENOMEM;
dev_err(&iface->dev, "Allocating URB buffer failed\n");
goto error;
}
/* Configure interrupt URB */
usb_fill_int_urb(pdata->urb, udev,
usb_rcvintpipe(udev, int_in_endpointAddr),
pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete,
pdata, 1);
if (usb_submit_urb(pdata->urb, GFP_KERNEL)) {
retval = -EIO;
dev_err(&iface->dev, "Submitting URB failed\n");
goto error;
}
/* Register backlight device */
snprintf(bl_name, sizeof(bl_name), "appledisplay%d",
atomic_inc_return(&count_displays) - 1);
pdata->bd = backlight_device_register(bl_name, NULL, pdata,
&appledisplay_bl_data);
if (IS_ERR(pdata->bd)) {
dev_err(&iface->dev, "Backlight registration failed\n");
goto error;
}
pdata->bd->props.max_brightness = 0xff;
/* Try to get brightness */
brightness = appledisplay_bl_get_brightness(pdata->bd);
if (brightness < 0) {
retval = brightness;
dev_err(&iface->dev,
"Error while getting initial brightness: %d\n", retval);
goto error;
}
/* Set brightness in backlight device */
pdata->bd->props.brightness = brightness;
/* save our data pointer in the interface device */
usb_set_intfdata(iface, pdata);
printk(KERN_INFO "appledisplay: Apple Cinema Display connected\n");
return 0;
error:
if (pdata) {
if (pdata->urb) {
usb_kill_urb(pdata->urb);
if (pdata->urbdata)
usb_buffer_free(pdata->udev, ACD_URB_BUFFER_LEN,
pdata->urbdata, pdata->urb->transfer_dma);
usb_free_urb(pdata->urb);
}
if (pdata->bd && !IS_ERR(pdata->bd))
backlight_device_unregister(pdata->bd);
kfree(pdata->msgdata);
}
usb_set_intfdata(iface, NULL);
kfree(pdata);
return retval;
}
static void appledisplay_disconnect(struct usb_interface *iface)
{
struct appledisplay *pdata = usb_get_intfdata(iface);
if (pdata) {
usb_kill_urb(pdata->urb);
cancel_delayed_work(&pdata->work);
backlight_device_unregister(pdata->bd);
usb_buffer_free(pdata->udev, ACD_URB_BUFFER_LEN,
pdata->urbdata, pdata->urb->transfer_dma);
usb_free_urb(pdata->urb);
kfree(pdata->msgdata);
kfree(pdata);
}
printk(KERN_INFO "appledisplay: Apple Cinema Display disconnected\n");
}
static struct usb_driver appledisplay_driver = {
.name = "appledisplay",
.probe = appledisplay_probe,
.disconnect = appledisplay_disconnect,
.id_table = appledisplay_table,
};
static int __init appledisplay_init(void)
{
wq = create_singlethread_workqueue("appledisplay");
if (!wq) {
printk(KERN_ERR "appledisplay: Could not create work queue\n");
return -ENOMEM;
}
return usb_register(&appledisplay_driver);
}
static void __exit appledisplay_exit(void)
{
flush_workqueue(wq);
destroy_workqueue(wq);
usb_deregister(&appledisplay_driver);
}
MODULE_AUTHOR("Michael Hanselmann");
MODULE_DESCRIPTION("Apple Cinema Display driver");
MODULE_LICENSE("GPL");
module_init(appledisplay_init);
module_exit(appledisplay_exit);

View File

@@ -0,0 +1,183 @@
/*
* USB BlackBerry charging module
*
* Copyright (C) 2007 Greg Kroah-Hartman <gregkh@suse.de>
*
* 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, version 2.
*
* Information on how to switch configs was taken by the bcharge.cc file
* created by the barry.sf.net project.
*
* bcharge.cc has the following copyright:
* Copyright (C) 2006, Net Direct Inc. (http://www.netdirect.ca/)
* and is released under the GPLv2.
*
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#define RIM_VENDOR 0x0fca
#define BLACKBERRY 0x0001
#define BLACKBERRY_PEARL_DUAL 0x0004
#define BLACKBERRY_PEARL 0x0006
static int debug;
static int pearl_dual_mode = 1;
#ifdef dbg
#undef dbg
#endif
#define dbg(dev, format, arg...) \
if (debug) \
dev_printk(KERN_DEBUG , dev , format , ## arg)
static struct usb_device_id id_table [] = {
{ USB_DEVICE(RIM_VENDOR, BLACKBERRY) },
{ USB_DEVICE(RIM_VENDOR, BLACKBERRY_PEARL) },
{ USB_DEVICE(RIM_VENDOR, BLACKBERRY_PEARL_DUAL) },
{ }, /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static int magic_charge(struct usb_device *udev)
{
char *dummy_buffer = kzalloc(2, GFP_KERNEL);
int retval;
if (!dummy_buffer)
return -ENOMEM;
/* send two magic commands and then set the configuration. The device
* will then reset itself with the new power usage and should start
* charging. */
/* Note, with testing, it only seems that the first message is really
* needed (at least for the 8700c), but to be safe, we emulate what
* other operating systems seem to be sending to their device. We
* really need to get some specs for this device to be sure about what
* is going on here.
*/
dbg(&udev->dev, "Sending first magic command\n");
retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0xa5, 0xc0, 0, 1, dummy_buffer, 2, 100);
if (retval != 2) {
dev_err(&udev->dev, "First magic command failed: %d.\n",
retval);
goto exit;
}
dbg(&udev->dev, "Sending second magic command\n");
retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0xa2, 0x40, 0, 1, dummy_buffer, 0, 100);
if (retval != 0) {
dev_err(&udev->dev, "Second magic command failed: %d.\n",
retval);
goto exit;
}
dbg(&udev->dev, "Calling set_configuration\n");
retval = usb_driver_set_configuration(udev, 1);
if (retval)
dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval);
exit:
kfree(dummy_buffer);
return retval;
}
static int magic_dual_mode(struct usb_device *udev)
{
char *dummy_buffer = kzalloc(2, GFP_KERNEL);
int retval;
if (!dummy_buffer)
return -ENOMEM;
/* send magic command so that the Blackberry Pearl device exposes
* two interfaces: both the USB mass-storage one and one which can
* be used for database access. */
dbg(&udev->dev, "Sending magic pearl command\n");
retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0xa9, 0xc0, 1, 1, dummy_buffer, 2, 100);
dbg(&udev->dev, "Magic pearl command returned %d\n", retval);
dbg(&udev->dev, "Calling set_configuration\n");
retval = usb_driver_set_configuration(udev, 1);
if (retval)
dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval);
kfree(dummy_buffer);
return retval;
}
static int berry_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
if (udev->bus_mA < 500) {
dbg(&udev->dev, "Not enough power to charge available\n");
return -ENODEV;
}
dbg(&udev->dev, "Power is set to %dmA\n",
udev->actconfig->desc.bMaxPower * 2);
/* check the power usage so we don't try to enable something that is
* already enabled */
if ((udev->actconfig->desc.bMaxPower * 2) == 500) {
dbg(&udev->dev, "device is already charging, power is "
"set to %dmA\n", udev->actconfig->desc.bMaxPower * 2);
return -ENODEV;
}
/* turn the power on */
magic_charge(udev);
if ((le16_to_cpu(udev->descriptor.idProduct) == BLACKBERRY_PEARL) &&
(pearl_dual_mode))
magic_dual_mode(udev);
/* we don't really want to bind to the device, userspace programs can
* handle the syncing just fine, so get outta here. */
return -ENODEV;
}
static void berry_disconnect(struct usb_interface *intf)
{
}
static struct usb_driver berry_driver = {
.name = "berry_charge",
.probe = berry_probe,
.disconnect = berry_disconnect,
.id_table = id_table,
};
static int __init berry_init(void)
{
return usb_register(&berry_driver);
}
static void __exit berry_exit(void)
{
usb_deregister(&berry_driver);
}
module_init(berry_init);
module_exit(berry_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
module_param(pearl_dual_mode, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(pearl_dual_mode, "Change Blackberry Pearl to run in dual mode");

View File

@@ -0,0 +1,298 @@
/*
* cypress_cy7c63.c
*
* Copyright (c) 2006-2007 Oliver Bock (bock@tfh-berlin.de)
*
* This driver is based on the Cypress USB Driver by Marcus Maul
* (cyport) and the 2.0 version of Greg Kroah-Hartman's
* USB Skeleton driver.
*
* This is a generic driver for the Cypress CY7C63xxx family.
* For the time being it enables you to read from and write to
* the single I/O ports of the device.
*
* Supported vendors: AK Modul-Bus Computer GmbH
* (Firmware "Port-Chip")
*
* Supported devices: CY7C63001A-PC
* CY7C63001C-PXC
* CY7C63001C-SXC
*
* Supported functions: Read/Write Ports
*
*
* For up-to-date information please visit:
* http://www.obock.de/kernel/cypress
*
* 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, version 2.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#define DRIVER_AUTHOR "Oliver Bock (bock@tfh-berlin.de)"
#define DRIVER_DESC "Cypress CY7C63xxx USB driver"
#define CYPRESS_VENDOR_ID 0xa2c
#define CYPRESS_PRODUCT_ID 0x8
#define CYPRESS_READ_PORT 0x4
#define CYPRESS_WRITE_PORT 0x5
#define CYPRESS_READ_RAM 0x2
#define CYPRESS_WRITE_RAM 0x3
#define CYPRESS_READ_ROM 0x1
#define CYPRESS_READ_PORT_ID0 0
#define CYPRESS_WRITE_PORT_ID0 0
#define CYPRESS_READ_PORT_ID1 0x2
#define CYPRESS_WRITE_PORT_ID1 1
#define CYPRESS_MAX_REQSIZE 8
/* table of devices that work with this driver */
static struct usb_device_id cypress_table [] = {
{ USB_DEVICE(CYPRESS_VENDOR_ID, CYPRESS_PRODUCT_ID) },
{ }
};
MODULE_DEVICE_TABLE(usb, cypress_table);
/* structure to hold all of our device specific stuff */
struct cypress {
struct usb_device * udev;
unsigned char port[2];
};
/* used to send usb control messages to device */
static int vendor_command(struct cypress *dev, unsigned char request,
unsigned char address, unsigned char data)
{
int retval = 0;
unsigned int pipe;
unsigned char *iobuf;
/* allocate some memory for the i/o buffer*/
iobuf = kzalloc(CYPRESS_MAX_REQSIZE, GFP_KERNEL);
if (!iobuf) {
dev_err(&dev->udev->dev, "Out of memory!\n");
retval = -ENOMEM;
goto error;
}
dev_dbg(&dev->udev->dev, "Sending usb_control_msg (data: %d)\n", data);
/* prepare usb control message and send it upstream */
pipe = usb_rcvctrlpipe(dev->udev, 0);
retval = usb_control_msg(dev->udev, pipe, request,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
address, data, iobuf, CYPRESS_MAX_REQSIZE,
USB_CTRL_GET_TIMEOUT);
/* store returned data (more READs to be added) */
switch (request) {
case CYPRESS_READ_PORT:
if (address == CYPRESS_READ_PORT_ID0) {
dev->port[0] = iobuf[1];
dev_dbg(&dev->udev->dev,
"READ_PORT0 returned: %d\n",
dev->port[0]);
}
else if (address == CYPRESS_READ_PORT_ID1) {
dev->port[1] = iobuf[1];
dev_dbg(&dev->udev->dev,
"READ_PORT1 returned: %d\n",
dev->port[1]);
}
break;
}
kfree(iobuf);
error:
return retval;
}
/* write port value */
static ssize_t write_port(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count,
int port_num, int write_id)
{
int value = -1;
int result = 0;
struct usb_interface *intf = to_usb_interface(dev);
struct cypress *cyp = usb_get_intfdata(intf);
dev_dbg(&cyp->udev->dev, "WRITE_PORT%d called\n", port_num);
/* validate input data */
if (sscanf(buf, "%d", &value) < 1) {
result = -EINVAL;
goto error;
}
if (value < 0 || value > 255) {
result = -EINVAL;
goto error;
}
result = vendor_command(cyp, CYPRESS_WRITE_PORT, write_id,
(unsigned char)value);
dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result);
error:
return result < 0 ? result : count;
}
/* attribute callback handler (write) */
static ssize_t set_port0_handler(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return write_port(dev, attr, buf, count, 0, CYPRESS_WRITE_PORT_ID0);
}
/* attribute callback handler (write) */
static ssize_t set_port1_handler(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return write_port(dev, attr, buf, count, 1, CYPRESS_WRITE_PORT_ID1);
}
/* read port value */
static ssize_t read_port(struct device *dev, struct device_attribute *attr,
char *buf, int port_num, int read_id)
{
int result = 0;
struct usb_interface *intf = to_usb_interface(dev);
struct cypress *cyp = usb_get_intfdata(intf);
dev_dbg(&cyp->udev->dev, "READ_PORT%d called\n", port_num);
result = vendor_command(cyp, CYPRESS_READ_PORT, read_id, 0);
dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result);
return sprintf(buf, "%d", cyp->port[port_num]);
}
/* attribute callback handler (read) */
static ssize_t get_port0_handler(struct device *dev,
struct device_attribute *attr, char *buf)
{
return read_port(dev, attr, buf, 0, CYPRESS_READ_PORT_ID0);
}
/* attribute callback handler (read) */
static ssize_t get_port1_handler(struct device *dev,
struct device_attribute *attr, char *buf)
{
return read_port(dev, attr, buf, 1, CYPRESS_READ_PORT_ID1);
}
static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR, get_port0_handler, set_port0_handler);
static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR, get_port1_handler, set_port1_handler);
static int cypress_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct cypress *dev = NULL;
int retval = -ENOMEM;
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory!\n");
goto error_mem;
}
dev->udev = usb_get_dev(interface_to_usbdev(interface));
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
/* create device attribute files */
retval = device_create_file(&interface->dev, &dev_attr_port0);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_port1);
if (retval)
goto error;
/* let the user know that the device is now attached */
dev_info(&interface->dev,
"Cypress CY7C63xxx device now attached\n");
return 0;
error:
device_remove_file(&interface->dev, &dev_attr_port0);
device_remove_file(&interface->dev, &dev_attr_port1);
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
error_mem:
return retval;
}
static void cypress_disconnect(struct usb_interface *interface)
{
struct cypress *dev;
dev = usb_get_intfdata(interface);
/* remove device attribute files */
device_remove_file(&interface->dev, &dev_attr_port0);
device_remove_file(&interface->dev, &dev_attr_port1);
/* the intfdata can be set to NULL only after the
* device files have been removed */
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
dev_info(&interface->dev,
"Cypress CY7C63xxx device now disconnected\n");
kfree(dev);
}
static struct usb_driver cypress_driver = {
.name = "cypress_cy7c63",
.probe = cypress_probe,
.disconnect = cypress_disconnect,
.id_table = cypress_table,
};
static int __init cypress_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&cypress_driver);
if (result)
printk(KERN_ERR KBUILD_MODNAME ": usb_register failed! "
"Error number: %d\n", result);
return result;
}
static void __exit cypress_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&cypress_driver);
}
module_init(cypress_init);
module_exit(cypress_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,447 @@
/* -*- linux-c -*-
* Cypress USB Thermometer driver
*
* Copyright (c) 2004 Erik Rigtorp <erkki@linux.nu> <erik@rigtorp.com>
*
* This driver works with Elektor magazine USB Interface as published in
* issue #291. It should also work with the original starter kit/demo board
* from Cypress.
*
* 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, version 2.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "Erik Rigtorp"
#define DRIVER_DESC "Cypress USB Thermometer driver"
#define USB_SKEL_VENDOR_ID 0x04b4
#define USB_SKEL_PRODUCT_ID 0x0002
static struct usb_device_id id_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ }
};
MODULE_DEVICE_TABLE (usb, id_table);
/* Structure to hold all of our device specific stuff */
struct usb_cytherm {
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface; /* the interface for this device */
int brightness;
};
/* local function prototypes */
static int cytherm_probe(struct usb_interface *interface,
const struct usb_device_id *id);
static void cytherm_disconnect(struct usb_interface *interface);
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver cytherm_driver = {
.name = "cytherm",
.probe = cytherm_probe,
.disconnect = cytherm_disconnect,
.id_table = id_table,
};
/* Vendor requests */
/* They all operate on one byte at a time */
#define PING 0x00
#define READ_ROM 0x01 /* Reads form ROM, value = address */
#define READ_RAM 0x02 /* Reads form RAM, value = address */
#define WRITE_RAM 0x03 /* Write to RAM, value = address, index = data */
#define READ_PORT 0x04 /* Reads from port, value = address */
#define WRITE_PORT 0x05 /* Write to port, value = address, index = data */
/* Send a vendor command to device */
static int vendor_command(struct usb_device *dev, unsigned char request,
unsigned char value, unsigned char index,
void *buf, int size)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
request,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
value,
index, buf, size,
USB_CTRL_GET_TIMEOUT);
}
#define BRIGHTNESS 0x2c /* RAM location for brightness value */
#define BRIGHTNESS_SEM 0x2b /* RAM location for brightness semaphore */
static ssize_t show_brightness(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
return sprintf(buf, "%i", cytherm->brightness);
}
static ssize_t set_brightness(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
unsigned char *buffer;
int retval;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&cytherm->udev->dev, "out of memory\n");
return 0;
}
cytherm->brightness = simple_strtoul(buf, NULL, 10);
if (cytherm->brightness > 0xFF)
cytherm->brightness = 0xFF;
else if (cytherm->brightness < 0)
cytherm->brightness = 0;
/* Set brightness */
retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS,
cytherm->brightness, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
/* Inform µC that we have changed the brightness setting */
retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS_SEM,
0x01, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
kfree(buffer);
return count;
}
static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR | S_IWGRP,
show_brightness, set_brightness);
#define TEMP 0x33 /* RAM location for temperature */
#define SIGN 0x34 /* RAM location for temperature sign */
static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
int retval;
unsigned char *buffer;
int temp, sign;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&cytherm->udev->dev, "out of memory\n");
return 0;
}
/* read temperature */
retval = vendor_command(cytherm->udev, READ_RAM, TEMP, 0, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
temp = buffer[1];
/* read sign */
retval = vendor_command(cytherm->udev, READ_RAM, SIGN, 0, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
sign = buffer[1];
kfree(buffer);
return sprintf(buf, "%c%i.%i", sign ? '-' : '+', temp >> 1,
5*(temp - ((temp >> 1) << 1)));
}
static ssize_t set_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return count;
}
static DEVICE_ATTR(temp, S_IRUGO, show_temp, set_temp);
#define BUTTON 0x7a
static ssize_t show_button(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
int retval;
unsigned char *buffer;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&cytherm->udev->dev, "out of memory\n");
return 0;
}
/* check button */
retval = vendor_command(cytherm->udev, READ_RAM, BUTTON, 0, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
retval = buffer[1];
kfree(buffer);
if (retval)
return sprintf(buf, "1");
else
return sprintf(buf, "0");
}
static ssize_t set_button(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return count;
}
static DEVICE_ATTR(button, S_IRUGO, show_button, set_button);
static ssize_t show_port0(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
int retval;
unsigned char *buffer;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&cytherm->udev->dev, "out of memory\n");
return 0;
}
retval = vendor_command(cytherm->udev, READ_PORT, 0, 0, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
retval = buffer[1];
kfree(buffer);
return sprintf(buf, "%d", retval);
}
static ssize_t set_port0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
unsigned char *buffer;
int retval;
int tmp;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&cytherm->udev->dev, "out of memory\n");
return 0;
}
tmp = simple_strtoul(buf, NULL, 10);
if (tmp > 0xFF)
tmp = 0xFF;
else if (tmp < 0)
tmp = 0;
retval = vendor_command(cytherm->udev, WRITE_PORT, 0,
tmp, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
kfree(buffer);
return count;
}
static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR | S_IWGRP, show_port0, set_port0);
static ssize_t show_port1(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
int retval;
unsigned char *buffer;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&cytherm->udev->dev, "out of memory\n");
return 0;
}
retval = vendor_command(cytherm->udev, READ_PORT, 1, 0, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
retval = buffer[1];
kfree(buffer);
return sprintf(buf, "%d", retval);
}
static ssize_t set_port1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_cytherm *cytherm = usb_get_intfdata(intf);
unsigned char *buffer;
int retval;
int tmp;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&cytherm->udev->dev, "out of memory\n");
return 0;
}
tmp = simple_strtoul(buf, NULL, 10);
if (tmp > 0xFF)
tmp = 0xFF;
else if (tmp < 0)
tmp = 0;
retval = vendor_command(cytherm->udev, WRITE_PORT, 1,
tmp, buffer, 8);
if (retval)
dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
kfree(buffer);
return count;
}
static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR | S_IWGRP, show_port1, set_port1);
static int cytherm_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_cytherm *dev = NULL;
int retval = -ENOMEM;
dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
if (dev == NULL) {
dev_err (&interface->dev, "Out of memory\n");
goto error_mem;
}
dev->udev = usb_get_dev(udev);
usb_set_intfdata (interface, dev);
dev->brightness = 0xFF;
retval = device_create_file(&interface->dev, &dev_attr_brightness);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_temp);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_button);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_port0);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_port1);
if (retval)
goto error;
dev_info (&interface->dev,
"Cypress thermometer device now attached\n");
return 0;
error:
device_remove_file(&interface->dev, &dev_attr_brightness);
device_remove_file(&interface->dev, &dev_attr_temp);
device_remove_file(&interface->dev, &dev_attr_button);
device_remove_file(&interface->dev, &dev_attr_port0);
device_remove_file(&interface->dev, &dev_attr_port1);
usb_set_intfdata (interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
error_mem:
return retval;
}
static void cytherm_disconnect(struct usb_interface *interface)
{
struct usb_cytherm *dev;
dev = usb_get_intfdata (interface);
device_remove_file(&interface->dev, &dev_attr_brightness);
device_remove_file(&interface->dev, &dev_attr_temp);
device_remove_file(&interface->dev, &dev_attr_button);
device_remove_file(&interface->dev, &dev_attr_port0);
device_remove_file(&interface->dev, &dev_attr_port1);
/* first remove the files, then NULL the pointer */
usb_set_intfdata (interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
dev_info(&interface->dev, "Cypress thermometer now disconnected\n");
}
static int __init usb_cytherm_init(void)
{
int result;
result = usb_register(&cytherm_driver);
if (result) {
printk(KERN_ERR KBUILD_MODNAME ": usb_register failed! "
"Error number: %d\n", result);
return result;
}
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return 0;
}
static void __exit usb_cytherm_exit(void)
{
usb_deregister(&cytherm_driver);
}
module_init (usb_cytherm_init);
module_exit (usb_cytherm_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,300 @@
/*
* Emagic EMI 2|6 usb audio interface firmware loader.
* Copyright (C) 2002
* Tapio Laxström (tapio.laxstrom@iptime.fi)
*
* 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, version 2.
*
* emi26.c,v 1.13 2002/03/08 13:10:26 tapio Exp
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/ihex.h>
#define EMI26_VENDOR_ID 0x086a /* Emagic Soft-und Hardware GmBH */
#define EMI26_PRODUCT_ID 0x0100 /* EMI 2|6 without firmware */
#define EMI26B_PRODUCT_ID 0x0102 /* EMI 2|6 without firmware */
#define ANCHOR_LOAD_INTERNAL 0xA0 /* Vendor specific request code for Anchor Upload/Download (This one is implemented in the core) */
#define ANCHOR_LOAD_EXTERNAL 0xA3 /* This command is not implemented in the core. Requires firmware */
#define ANCHOR_LOAD_FPGA 0xA5 /* This command is not implemented in the core. Requires firmware. Emagic extension */
#define MAX_INTERNAL_ADDRESS 0x1B3F /* This is the highest internal RAM address for the AN2131Q */
#define CPUCS_REG 0x7F92 /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
#define INTERNAL_RAM(address) (address <= MAX_INTERNAL_ADDRESS)
static int emi26_writememory( struct usb_device *dev, int address,
const unsigned char *data, int length,
__u8 bRequest);
static int emi26_set_reset(struct usb_device *dev, unsigned char reset_bit);
static int emi26_load_firmware (struct usb_device *dev);
static int emi26_probe(struct usb_interface *intf, const struct usb_device_id *id);
static void emi26_disconnect(struct usb_interface *intf);
static int __init emi26_init (void);
static void __exit emi26_exit (void);
/* thanks to drivers/usb/serial/keyspan_pda.c code */
static int emi26_writememory (struct usb_device *dev, int address,
const unsigned char *data, int length,
__u8 request)
{
int result;
unsigned char *buffer = kmemdup(data, length, GFP_KERNEL);
if (!buffer) {
dev_err(&dev->dev, "kmalloc(%d) failed.\n", length);
return -ENOMEM;
}
/* Note: usb_control_msg returns negative value on error or length of the
* data that was written! */
result = usb_control_msg (dev, usb_sndctrlpipe(dev, 0), request, 0x40, address, 0, buffer, length, 300);
kfree (buffer);
return result;
}
/* thanks to drivers/usb/serial/keyspan_pda.c code */
static int emi26_set_reset (struct usb_device *dev, unsigned char reset_bit)
{
int response;
dev_info(&dev->dev, "%s - %d\n", __func__, reset_bit);
/* printk(KERN_DEBUG "%s - %d", __func__, reset_bit); */
response = emi26_writememory (dev, CPUCS_REG, &reset_bit, 1, 0xa0);
if (response < 0) {
dev_err(&dev->dev, "set_reset (%d) failed\n", reset_bit);
}
return response;
}
#define FW_LOAD_SIZE 1023
static int emi26_load_firmware (struct usb_device *dev)
{
const struct firmware *loader_fw = NULL;
const struct firmware *bitstream_fw = NULL;
const struct firmware *firmware_fw = NULL;
const struct ihex_binrec *rec;
int err;
int i;
__u32 addr; /* Address to write */
__u8 *buf;
buf = kmalloc(FW_LOAD_SIZE, GFP_KERNEL);
if (!buf) {
dev_err(&dev->dev, "%s - error loading firmware: error = %d\n",
__func__, -ENOMEM);
err = -ENOMEM;
goto wraperr;
}
err = request_ihex_firmware(&loader_fw, "emi26/loader.fw", &dev->dev);
if (err)
goto nofw;
err = request_ihex_firmware(&bitstream_fw, "emi26/bitstream.fw",
&dev->dev);
if (err)
goto nofw;
err = request_ihex_firmware(&firmware_fw, "emi26/firmware.fw",
&dev->dev);
if (err) {
nofw:
dev_err(&dev->dev, "%s - request_firmware() failed\n",
__func__);
goto wraperr;
}
/* Assert reset (stop the CPU in the EMI) */
err = emi26_set_reset(dev,1);
if (err < 0) {
dev_err(&dev->dev,"%s - error loading firmware: error = %d\n",
__func__, err);
goto wraperr;
}
rec = (const struct ihex_binrec *)loader_fw->data;
/* 1. We need to put the loader for the FPGA into the EZ-USB */
while (rec) {
err = emi26_writememory(dev, be32_to_cpu(rec->addr),
rec->data, be16_to_cpu(rec->len),
ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
rec = ihex_next_binrec(rec);
}
/* De-assert reset (let the CPU run) */
err = emi26_set_reset(dev,0);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
msleep(250); /* let device settle */
/* 2. We upload the FPGA firmware into the EMI
* Note: collect up to 1023 (yes!) bytes and send them with
* a single request. This is _much_ faster! */
rec = (const struct ihex_binrec *)bitstream_fw->data;
do {
i = 0;
addr = be32_to_cpu(rec->addr);
/* intel hex records are terminated with type 0 element */
while (rec && (i + be16_to_cpu(rec->len) < FW_LOAD_SIZE)) {
memcpy(buf + i, rec->data, be16_to_cpu(rec->len));
i += be16_to_cpu(rec->len);
rec = ihex_next_binrec(rec);
}
err = emi26_writememory(dev, addr, buf, i, ANCHOR_LOAD_FPGA);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
} while (rec);
/* Assert reset (stop the CPU in the EMI) */
err = emi26_set_reset(dev,1);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
/* 3. We need to put the loader for the firmware into the EZ-USB (again...) */
for (rec = (const struct ihex_binrec *)loader_fw->data;
rec; rec = ihex_next_binrec(rec)) {
err = emi26_writememory(dev, be32_to_cpu(rec->addr),
rec->data, be16_to_cpu(rec->len),
ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
}
msleep(250); /* let device settle */
/* De-assert reset (let the CPU run) */
err = emi26_set_reset(dev,0);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
/* 4. We put the part of the firmware that lies in the external RAM into the EZ-USB */
for (rec = (const struct ihex_binrec *)firmware_fw->data;
rec; rec = ihex_next_binrec(rec)) {
if (!INTERNAL_RAM(be32_to_cpu(rec->addr))) {
err = emi26_writememory(dev, be32_to_cpu(rec->addr),
rec->data, be16_to_cpu(rec->len),
ANCHOR_LOAD_EXTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
}
}
/* Assert reset (stop the CPU in the EMI) */
err = emi26_set_reset(dev,1);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
for (rec = (const struct ihex_binrec *)firmware_fw->data;
rec; rec = ihex_next_binrec(rec)) {
if (INTERNAL_RAM(be32_to_cpu(rec->addr))) {
err = emi26_writememory(dev, be32_to_cpu(rec->addr),
rec->data, be16_to_cpu(rec->len),
ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
}
}
/* De-assert reset (let the CPU run) */
err = emi26_set_reset(dev,0);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
msleep(250); /* let device settle */
/* return 1 to fail the driver inialization
* and give real driver change to load */
err = 1;
wraperr:
release_firmware(loader_fw);
release_firmware(bitstream_fw);
release_firmware(firmware_fw);
kfree(buf);
return err;
}
static struct usb_device_id id_table [] = {
{ USB_DEVICE(EMI26_VENDOR_ID, EMI26_PRODUCT_ID) },
{ USB_DEVICE(EMI26_VENDOR_ID, EMI26B_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, id_table);
static int emi26_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
dev_info(&intf->dev, "%s start\n", __func__);
emi26_load_firmware(dev);
/* do not return the driver context, let real audio driver do that */
return -EIO;
}
static void emi26_disconnect(struct usb_interface *intf)
{
}
static struct usb_driver emi26_driver = {
.name = "emi26 - firmware loader",
.probe = emi26_probe,
.disconnect = emi26_disconnect,
.id_table = id_table,
};
static int __init emi26_init (void)
{
return usb_register(&emi26_driver);
}
static void __exit emi26_exit (void)
{
usb_deregister (&emi26_driver);
}
module_init(emi26_init);
module_exit(emi26_exit);
MODULE_AUTHOR("Tapio Laxström");
MODULE_DESCRIPTION("Emagic EMI 2|6 firmware loader.");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("emi26/loader.fw");
MODULE_FIRMWARE("emi26/bitstream.fw");
MODULE_FIRMWARE("emi26/firmware.fw");
/* vi:ai:syntax=c:sw=8:ts=8:tw=80
*/

View File

@@ -0,0 +1,318 @@
/*
* Emagic EMI 2|6 usb audio interface firmware loader.
* Copyright (C) 2002
* Tapio Laxström (tapio.laxstrom@iptime.fi)
*
* 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, version 2.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/ihex.h>
/* include firmware (variables)*/
/* FIXME: This is quick and dirty solution! */
#define SPDIF /* if you want SPDIF comment next line */
//#undef SPDIF /* if you want MIDI uncomment this line */
#ifdef SPDIF
#define FIRMWARE_FW "emi62/spdif.fw"
#else
#define FIRMWARE_FW "emi62/midi.fw"
#endif
#define EMI62_VENDOR_ID 0x086a /* Emagic Soft-und Hardware GmBH */
#define EMI62_PRODUCT_ID 0x0110 /* EMI 6|2m without firmware */
#define ANCHOR_LOAD_INTERNAL 0xA0 /* Vendor specific request code for Anchor Upload/Download (This one is implemented in the core) */
#define ANCHOR_LOAD_EXTERNAL 0xA3 /* This command is not implemented in the core. Requires firmware */
#define ANCHOR_LOAD_FPGA 0xA5 /* This command is not implemented in the core. Requires firmware. Emagic extension */
#define MAX_INTERNAL_ADDRESS 0x1B3F /* This is the highest internal RAM address for the AN2131Q */
#define CPUCS_REG 0x7F92 /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
#define INTERNAL_RAM(address) (address <= MAX_INTERNAL_ADDRESS)
static int emi62_writememory(struct usb_device *dev, int address,
const unsigned char *data, int length,
__u8 bRequest);
static int emi62_set_reset(struct usb_device *dev, unsigned char reset_bit);
static int emi62_load_firmware (struct usb_device *dev);
static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *id);
static void emi62_disconnect(struct usb_interface *intf);
static int __init emi62_init (void);
static void __exit emi62_exit (void);
/* thanks to drivers/usb/serial/keyspan_pda.c code */
static int emi62_writememory(struct usb_device *dev, int address,
const unsigned char *data, int length,
__u8 request)
{
int result;
unsigned char *buffer = kmemdup(data, length, GFP_KERNEL);
if (!buffer) {
err("emi62: kmalloc(%d) failed.", length);
return -ENOMEM;
}
/* Note: usb_control_msg returns negative value on error or length of the
* data that was written! */
result = usb_control_msg (dev, usb_sndctrlpipe(dev, 0), request, 0x40, address, 0, buffer, length, 300);
kfree (buffer);
return result;
}
/* thanks to drivers/usb/serial/keyspan_pda.c code */
static int emi62_set_reset (struct usb_device *dev, unsigned char reset_bit)
{
int response;
dev_info(&dev->dev, "%s - %d\n", __func__, reset_bit);
response = emi62_writememory (dev, CPUCS_REG, &reset_bit, 1, 0xa0);
if (response < 0) {
err("emi62: set_reset (%d) failed", reset_bit);
}
return response;
}
#define FW_LOAD_SIZE 1023
static int emi62_load_firmware (struct usb_device *dev)
{
const struct firmware *loader_fw = NULL;
const struct firmware *bitstream_fw = NULL;
const struct firmware *firmware_fw = NULL;
const struct ihex_binrec *rec;
int err;
int i;
__u32 addr; /* Address to write */
__u8 *buf;
dev_dbg(&dev->dev, "load_firmware\n");
buf = kmalloc(FW_LOAD_SIZE, GFP_KERNEL);
if (!buf) {
err( "%s - error loading firmware: error = %d", __func__, -ENOMEM);
err = -ENOMEM;
goto wraperr;
}
err = request_ihex_firmware(&loader_fw, "emi62/loader.fw", &dev->dev);
if (err)
goto nofw;
err = request_ihex_firmware(&bitstream_fw, "emi62/bitstream.fw",
&dev->dev);
if (err)
goto nofw;
err = request_ihex_firmware(&firmware_fw, FIRMWARE_FW, &dev->dev);
if (err) {
nofw:
err( "%s - request_firmware() failed", __func__);
goto wraperr;
}
/* Assert reset (stop the CPU in the EMI) */
err = emi62_set_reset(dev,1);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
rec = (const struct ihex_binrec *)loader_fw->data;
/* 1. We need to put the loader for the FPGA into the EZ-USB */
while (rec) {
err = emi62_writememory(dev, be32_to_cpu(rec->addr),
rec->data, be16_to_cpu(rec->len),
ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
rec = ihex_next_binrec(rec);
}
/* De-assert reset (let the CPU run) */
err = emi62_set_reset(dev,0);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
msleep(250); /* let device settle */
/* 2. We upload the FPGA firmware into the EMI
* Note: collect up to 1023 (yes!) bytes and send them with
* a single request. This is _much_ faster! */
rec = (const struct ihex_binrec *)bitstream_fw->data;
do {
i = 0;
addr = be32_to_cpu(rec->addr);
/* intel hex records are terminated with type 0 element */
while (rec && (i + be16_to_cpu(rec->len) < FW_LOAD_SIZE)) {
memcpy(buf + i, rec->data, be16_to_cpu(rec->len));
i += be16_to_cpu(rec->len);
rec = ihex_next_binrec(rec);
}
err = emi62_writememory(dev, addr, buf, i, ANCHOR_LOAD_FPGA);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
} while (rec);
/* Assert reset (stop the CPU in the EMI) */
err = emi62_set_reset(dev,1);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
/* 3. We need to put the loader for the firmware into the EZ-USB (again...) */
for (rec = (const struct ihex_binrec *)loader_fw->data;
rec; rec = ihex_next_binrec(rec)) {
err = emi62_writememory(dev, be32_to_cpu(rec->addr),
rec->data, be16_to_cpu(rec->len),
ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
}
/* De-assert reset (let the CPU run) */
err = emi62_set_reset(dev,0);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
msleep(250); /* let device settle */
/* 4. We put the part of the firmware that lies in the external RAM into the EZ-USB */
for (rec = (const struct ihex_binrec *)firmware_fw->data;
rec; rec = ihex_next_binrec(rec)) {
if (!INTERNAL_RAM(be32_to_cpu(rec->addr))) {
err = emi62_writememory(dev, be32_to_cpu(rec->addr),
rec->data, be16_to_cpu(rec->len),
ANCHOR_LOAD_EXTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
}
}
/* Assert reset (stop the CPU in the EMI) */
err = emi62_set_reset(dev,1);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
for (rec = (const struct ihex_binrec *)firmware_fw->data;
rec; rec = ihex_next_binrec(rec)) {
if (INTERNAL_RAM(be32_to_cpu(rec->addr))) {
err = emi62_writememory(dev, be32_to_cpu(rec->addr),
rec->data, be16_to_cpu(rec->len),
ANCHOR_LOAD_EXTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
}
}
/* De-assert reset (let the CPU run) */
err = emi62_set_reset(dev,0);
if (err < 0) {
err("%s - error loading firmware: error = %d", __func__, err);
goto wraperr;
}
msleep(250); /* let device settle */
release_firmware(loader_fw);
release_firmware(bitstream_fw);
release_firmware(firmware_fw);
kfree(buf);
/* return 1 to fail the driver inialization
* and give real driver change to load */
return 1;
wraperr:
release_firmware(loader_fw);
release_firmware(bitstream_fw);
release_firmware(firmware_fw);
kfree(buf);
dev_err(&dev->dev, "Error\n");
return err;
}
static __devinitdata struct usb_device_id id_table [] = {
{ USB_DEVICE(EMI62_VENDOR_ID, EMI62_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, id_table);
static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
dev_dbg(&intf->dev, "emi62_probe\n");
dev_info(&intf->dev, "%s start\n", __func__);
emi62_load_firmware(dev);
/* do not return the driver context, let real audio driver do that */
return -EIO;
}
static void emi62_disconnect(struct usb_interface *intf)
{
}
static struct usb_driver emi62_driver = {
.name = "emi62 - firmware loader",
.probe = emi62_probe,
.disconnect = emi62_disconnect,
.id_table = id_table,
};
static int __init emi62_init (void)
{
int retval;
retval = usb_register (&emi62_driver);
if (retval)
printk(KERN_ERR "adi-emi: registration failed\n");
return retval;
}
static void __exit emi62_exit (void)
{
usb_deregister (&emi62_driver);
}
module_init(emi62_init);
module_exit(emi62_exit);
MODULE_AUTHOR("Tapio Laxström");
MODULE_DESCRIPTION("Emagic EMI 6|2m firmware loader.");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("emi62/loader.fw");
MODULE_FIRMWARE("emi62/bitstream.fw");
MODULE_FIRMWARE(FIRMWARE_FW);
/* vi:ai:syntax=c:sw=8:ts=8:tw=80
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,457 @@
/* Siemens ID Mouse driver v0.6
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.
Copyright (C) 2004-5 by Florian 'Floe' Echtler <echtler@fs.tum.de>
and Andreas 'ad' Deresch <aderesch@fs.tum.de>
Derived from the USB Skeleton driver 1.1,
Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
Additional information provided by Martin Reising
<Martin.Reising@natural-computing.de>
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
/* image constants */
#define WIDTH 225
#define HEIGHT 289
#define HEADER "P5 225 289 255 "
#define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1)
/* version information */
#define DRIVER_VERSION "0.6"
#define DRIVER_SHORT "idmouse"
#define DRIVER_AUTHOR "Florian 'Floe' Echtler <echtler@fs.tum.de>"
#define DRIVER_DESC "Siemens ID Mouse FingerTIP Sensor Driver"
/* minor number for misc USB devices */
#define USB_IDMOUSE_MINOR_BASE 132
/* vendor and device IDs */
#define ID_SIEMENS 0x0681
#define ID_IDMOUSE 0x0005
#define ID_CHERRY 0x0010
/* device ID table */
static struct usb_device_id idmouse_table[] = {
{USB_DEVICE(ID_SIEMENS, ID_IDMOUSE)}, /* Siemens ID Mouse (Professional) */
{USB_DEVICE(ID_SIEMENS, ID_CHERRY )}, /* Cherry FingerTIP ID Board */
{} /* terminating null entry */
};
/* sensor commands */
#define FTIP_RESET 0x20
#define FTIP_ACQUIRE 0x21
#define FTIP_RELEASE 0x22
#define FTIP_BLINK 0x23 /* LSB of value = blink pulse width */
#define FTIP_SCROLL 0x24
#define ftip_command(dev, command, value, index) \
usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), command, \
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000)
MODULE_DEVICE_TABLE(usb, idmouse_table);
static DEFINE_MUTEX(open_disc_mutex);
/* structure to hold all of our device specific stuff */
struct usb_idmouse {
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface; /* the interface for this device */
unsigned char *bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the maximum bulk packet size */
size_t orig_bi_size; /* same as above, but reported by the device */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
int open; /* if the port is open or not */
int present; /* if the device is not disconnected */
struct mutex lock; /* locks this structure */
};
/* local function prototypes */
static ssize_t idmouse_read(struct file *file, char __user *buffer,
size_t count, loff_t * ppos);
static int idmouse_open(struct inode *inode, struct file *file);
static int idmouse_release(struct inode *inode, struct file *file);
static int idmouse_probe(struct usb_interface *interface,
const struct usb_device_id *id);
static void idmouse_disconnect(struct usb_interface *interface);
static int idmouse_suspend(struct usb_interface *intf, pm_message_t message);
static int idmouse_resume(struct usb_interface *intf);
/* file operation pointers */
static const struct file_operations idmouse_fops = {
.owner = THIS_MODULE,
.read = idmouse_read,
.open = idmouse_open,
.release = idmouse_release,
};
/* class driver information */
static struct usb_class_driver idmouse_class = {
.name = "idmouse%d",
.fops = &idmouse_fops,
.minor_base = USB_IDMOUSE_MINOR_BASE,
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver idmouse_driver = {
.name = DRIVER_SHORT,
.probe = idmouse_probe,
.disconnect = idmouse_disconnect,
.suspend = idmouse_suspend,
.resume = idmouse_resume,
.reset_resume = idmouse_resume,
.id_table = idmouse_table,
.supports_autosuspend = 1,
};
static int idmouse_create_image(struct usb_idmouse *dev)
{
int bytes_read;
int bulk_read;
int result;
memcpy(dev->bulk_in_buffer, HEADER, sizeof(HEADER)-1);
bytes_read = sizeof(HEADER)-1;
/* reset the device and set a fast blink rate */
result = ftip_command(dev, FTIP_RELEASE, 0, 0);
if (result < 0)
goto reset;
result = ftip_command(dev, FTIP_BLINK, 1, 0);
if (result < 0)
goto reset;
/* initialize the sensor - sending this command twice */
/* significantly reduces the rate of failed reads */
result = ftip_command(dev, FTIP_ACQUIRE, 0, 0);
if (result < 0)
goto reset;
result = ftip_command(dev, FTIP_ACQUIRE, 0, 0);
if (result < 0)
goto reset;
/* start the readout - sending this command twice */
/* presumably enables the high dynamic range mode */
result = ftip_command(dev, FTIP_RESET, 0, 0);
if (result < 0)
goto reset;
result = ftip_command(dev, FTIP_RESET, 0, 0);
if (result < 0)
goto reset;
/* loop over a blocking bulk read to get data from the device */
while (bytes_read < IMGSIZE) {
result = usb_bulk_msg (dev->udev,
usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer + bytes_read,
dev->bulk_in_size, &bulk_read, 5000);
if (result < 0) {
/* Maybe this error was caused by the increased packet size? */
/* Reset to the original value and tell userspace to retry. */
if (dev->bulk_in_size != dev->orig_bi_size) {
dev->bulk_in_size = dev->orig_bi_size;
result = -EAGAIN;
}
break;
}
if (signal_pending(current)) {
result = -EINTR;
break;
}
bytes_read += bulk_read;
}
/* reset the device */
reset:
ftip_command(dev, FTIP_RELEASE, 0, 0);
/* check for valid image */
/* right border should be black (0x00) */
for (bytes_read = sizeof(HEADER)-1 + WIDTH-1; bytes_read < IMGSIZE; bytes_read += WIDTH)
if (dev->bulk_in_buffer[bytes_read] != 0x00)
return -EAGAIN;
/* lower border should be white (0xFF) */
for (bytes_read = IMGSIZE-WIDTH; bytes_read < IMGSIZE-1; bytes_read++)
if (dev->bulk_in_buffer[bytes_read] != 0xFF)
return -EAGAIN;
/* should be IMGSIZE == 65040 */
dbg("read %d bytes fingerprint data", bytes_read);
return result;
}
/* PM operations are nops as this driver does IO only during open() */
static int idmouse_suspend(struct usb_interface *intf, pm_message_t message)
{
return 0;
}
static int idmouse_resume(struct usb_interface *intf)
{
return 0;
}
static inline void idmouse_delete(struct usb_idmouse *dev)
{
kfree(dev->bulk_in_buffer);
kfree(dev);
}
static int idmouse_open(struct inode *inode, struct file *file)
{
struct usb_idmouse *dev;
struct usb_interface *interface;
int result;
/* get the interface from minor number and driver information */
interface = usb_find_interface (&idmouse_driver, iminor (inode));
if (!interface)
return -ENODEV;
mutex_lock(&open_disc_mutex);
/* get the device information block from the interface */
dev = usb_get_intfdata(interface);
if (!dev) {
mutex_unlock(&open_disc_mutex);
return -ENODEV;
}
/* lock this device */
mutex_lock(&dev->lock);
mutex_unlock(&open_disc_mutex);
/* check if already open */
if (dev->open) {
/* already open, so fail */
result = -EBUSY;
} else {
/* create a new image and check for success */
result = usb_autopm_get_interface(interface);
if (result)
goto error;
result = idmouse_create_image (dev);
if (result)
goto error;
usb_autopm_put_interface(interface);
/* increment our usage count for the driver */
++dev->open;
/* save our object in the file's private structure */
file->private_data = dev;
}
error:
/* unlock this device */
mutex_unlock(&dev->lock);
return result;
}
static int idmouse_release(struct inode *inode, struct file *file)
{
struct usb_idmouse *dev;
dev = file->private_data;
if (dev == NULL)
return -ENODEV;
mutex_lock(&open_disc_mutex);
/* lock our device */
mutex_lock(&dev->lock);
/* are we really open? */
if (dev->open <= 0) {
mutex_unlock(&dev->lock);
mutex_unlock(&open_disc_mutex);
return -ENODEV;
}
--dev->open;
if (!dev->present) {
/* the device was unplugged before the file was released */
mutex_unlock(&dev->lock);
mutex_unlock(&open_disc_mutex);
idmouse_delete(dev);
} else {
mutex_unlock(&dev->lock);
mutex_unlock(&open_disc_mutex);
}
return 0;
}
static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count,
loff_t * ppos)
{
struct usb_idmouse *dev = file->private_data;
int result;
/* lock this object */
mutex_lock(&dev->lock);
/* verify that the device wasn't unplugged */
if (!dev->present) {
mutex_unlock(&dev->lock);
return -ENODEV;
}
result = simple_read_from_buffer(buffer, count, ppos,
dev->bulk_in_buffer, IMGSIZE);
/* unlock the device */
mutex_unlock(&dev->lock);
return result;
}
static int idmouse_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_idmouse *dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int result;
/* check if we have gotten the data or the hid interface */
iface_desc = &interface->altsetting[0];
if (iface_desc->desc.bInterfaceClass != 0x0A)
return -ENODEV;
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL)
return -ENOMEM;
mutex_init(&dev->lock);
dev->udev = udev;
dev->interface = interface;
/* set up the endpoint information - use only the first bulk-in endpoint */
endpoint = &iface_desc->endpoint[0].desc;
if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
dev->orig_bi_size = le16_to_cpu(endpoint->wMaxPacketSize);
dev->bulk_in_size = 0x200; /* works _much_ faster */
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer =
kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
err("Unable to allocate input buffer.");
idmouse_delete(dev);
return -ENOMEM;
}
}
if (!(dev->bulk_in_endpointAddr)) {
err("Unable to find bulk-in endpoint.");
idmouse_delete(dev);
return -ENODEV;
}
/* allow device read, write and ioctl */
dev->present = 1;
/* we can register the device now, as it is ready */
usb_set_intfdata(interface, dev);
result = usb_register_dev(interface, &idmouse_class);
if (result) {
/* something prevented us from registering this device */
err("Unble to allocate minor number.");
usb_set_intfdata(interface, NULL);
idmouse_delete(dev);
return result;
}
/* be noisy */
dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
return 0;
}
static void idmouse_disconnect(struct usb_interface *interface)
{
struct usb_idmouse *dev;
/* get device structure */
dev = usb_get_intfdata(interface);
/* give back our minor */
usb_deregister_dev(interface, &idmouse_class);
mutex_lock(&open_disc_mutex);
usb_set_intfdata(interface, NULL);
/* lock the device */
mutex_lock(&dev->lock);
mutex_unlock(&open_disc_mutex);
/* prevent device read, write and ioctl */
dev->present = 0;
/* if the device is opened, idmouse_release will clean this up */
if (!dev->open) {
mutex_unlock(&dev->lock);
idmouse_delete(dev);
} else {
/* unlock */
mutex_unlock(&dev->lock);
}
dev_info(&interface->dev, "disconnected\n");
}
static int __init usb_idmouse_init(void)
{
int result;
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
/* register this driver with the USB subsystem */
result = usb_register(&idmouse_driver);
if (result)
err("Unable to register device (error %d).", result);
return result;
}
static void __exit usb_idmouse_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&idmouse_driver);
}
module_init(usb_idmouse_init);
module_exit(usb_idmouse_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,935 @@
/*
* Native support for the I/O-Warrior USB devices
*
* Copyright (c) 2003-2005 Code Mercenaries GmbH
* written by Christian Lucht <lucht@codemercs.com>
*
* based on
* usb-skeleton.c by Greg Kroah-Hartman <greg@kroah.com>
* brlvger.c by Stephane Dalton <sdalton@videotron.ca>
* and St<53>hane Doyon <s.doyon@videotron.ca>
*
* Released under the GPLv2.
*/
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/poll.h>
#include <linux/usb/iowarrior.h>
/* Version Information */
#define DRIVER_VERSION "v0.4.0"
#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"
#define DRIVER_DESC "USB IO-Warrior driver (Linux 2.6.x)"
#define USB_VENDOR_ID_CODEMERCS 1984
/* low speed iowarrior */
#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
#define USB_DEVICE_ID_CODEMERCS_IOWPV1 0x1511
#define USB_DEVICE_ID_CODEMERCS_IOWPV2 0x1512
/* full speed iowarrior */
#define USB_DEVICE_ID_CODEMERCS_IOW56 0x1503
/* Get a minor range for your devices from the usb maintainer */
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define IOWARRIOR_MINOR_BASE 0
#else
#define IOWARRIOR_MINOR_BASE 208 // SKELETON_MINOR_BASE 192 + 16, not offical yet
#endif
/* interrupt input queue size */
#define MAX_INTERRUPT_BUFFER 16
/*
maximum number of urbs that are submitted for writes at the same time,
this applies to the IOWarrior56 only!
IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls.
*/
#define MAX_WRITES_IN_FLIGHT 4
/* Use our own dbg macro */
#undef dbg
#define dbg( format, arg... ) do { if( debug ) printk( KERN_DEBUG __FILE__ ": " format "\n" , ## arg ); } while ( 0 )
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/* Module parameters */
static int debug = 0;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "debug=1 enables debugging messages");
static struct usb_driver iowarrior_driver;
static DEFINE_MUTEX(iowarrior_open_disc_lock);
/*--------------*/
/* data */
/*--------------*/
/* Structure to hold all of our device specific stuff */
struct iowarrior {
struct mutex mutex; /* locks this structure */
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface; /* the interface for this device */
unsigned char minor; /* the starting minor number for this device */
struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */
struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */
struct urb *int_in_urb; /* the urb for reading data */
unsigned char *int_in_buffer; /* buffer for data to be read */
unsigned char serial_number; /* to detect lost packages */
unsigned char *read_queue; /* size is MAX_INTERRUPT_BUFFER * packet size */
wait_queue_head_t read_wait;
wait_queue_head_t write_wait; /* wait-queue for writing to the device */
atomic_t write_busy; /* number of write-urbs submitted */
atomic_t read_idx;
atomic_t intr_idx;
spinlock_t intr_idx_lock; /* protects intr_idx */
atomic_t overflow_flag; /* signals an index 'rollover' */
int present; /* this is 1 as long as the device is connected */
int opened; /* this is 1 if the device is currently open */
char chip_serial[9]; /* the serial number string of the chip connected */
int report_size; /* number of bytes in a report */
u16 product_id;
};
/*--------------*/
/* globals */
/*--------------*/
/*
* USB spec identifies 5 second timeouts.
*/
#define GET_TIMEOUT 5
#define USB_REQ_GET_REPORT 0x01
//#if 0
static int usb_get_report(struct usb_device *dev,
struct usb_host_interface *inter, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_REPORT,
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, (type << 8) + id,
inter->desc.bInterfaceNumber, buf, size,
GET_TIMEOUT*HZ);
}
//#endif
#define USB_REQ_SET_REPORT 0x09
static int usb_set_report(struct usb_interface *intf, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(interface_to_usbdev(intf),
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
USB_REQ_SET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(type << 8) + id,
intf->cur_altsetting->desc.bInterfaceNumber, buf,
size, HZ);
}
/*---------------------*/
/* driver registration */
/*---------------------*/
/* table of devices that work with this driver */
static struct usb_device_id iowarrior_ids[] = {
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)},
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)},
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)},
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)},
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)},
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, iowarrior_ids);
/*
* USB callback handler for reading data
*/
static void iowarrior_callback(struct urb *urb)
{
struct iowarrior *dev = urb->context;
int intr_idx;
int read_idx;
int aux_idx;
int offset;
int status = urb->status;
int retval;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
return;
default:
goto exit;
}
spin_lock(&dev->intr_idx_lock);
intr_idx = atomic_read(&dev->intr_idx);
/* aux_idx become previous intr_idx */
aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1);
read_idx = atomic_read(&dev->read_idx);
/* queue is not empty and it's interface 0 */
if ((intr_idx != read_idx)
&& (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) {
/* + 1 for serial number */
offset = aux_idx * (dev->report_size + 1);
if (!memcmp
(dev->read_queue + offset, urb->transfer_buffer,
dev->report_size)) {
/* equal values on interface 0 will be ignored */
spin_unlock(&dev->intr_idx_lock);
goto exit;
}
}
/* aux_idx become next intr_idx */
aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1);
if (read_idx == aux_idx) {
/* queue full, dropping oldest input */
read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx;
atomic_set(&dev->read_idx, read_idx);
atomic_set(&dev->overflow_flag, 1);
}
/* +1 for serial number */
offset = intr_idx * (dev->report_size + 1);
memcpy(dev->read_queue + offset, urb->transfer_buffer,
dev->report_size);
*(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++;
atomic_set(&dev->intr_idx, aux_idx);
spin_unlock(&dev->intr_idx_lock);
/* tell the blocking read about the new data */
wake_up_interruptible(&dev->read_wait);
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d\n",
__func__, retval);
}
/*
* USB Callback handler for write-ops
*/
static void iowarrior_write_callback(struct urb *urb)
{
struct iowarrior *dev;
int status = urb->status;
dev = urb->context;
/* sync/async unlink faults aren't errors */
if (status &&
!(status == -ENOENT ||
status == -ECONNRESET || status == -ESHUTDOWN)) {
dbg("%s - nonzero write bulk status received: %d",
__func__, status);
}
/* free up our allocated buffer */
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
/* tell a waiting writer the interrupt-out-pipe is available again */
atomic_dec(&dev->write_busy);
wake_up_interruptible(&dev->write_wait);
}
/**
* iowarrior_delete
*/
static inline void iowarrior_delete(struct iowarrior *dev)
{
dbg("%s - minor %d", __func__, dev->minor);
kfree(dev->int_in_buffer);
usb_free_urb(dev->int_in_urb);
kfree(dev->read_queue);
kfree(dev);
}
/*---------------------*/
/* fops implementation */
/*---------------------*/
static int read_index(struct iowarrior *dev)
{
int intr_idx, read_idx;
read_idx = atomic_read(&dev->read_idx);
intr_idx = atomic_read(&dev->intr_idx);
return (read_idx == intr_idx ? -1 : read_idx);
}
/**
* iowarrior_read
*/
static ssize_t iowarrior_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct iowarrior *dev;
int read_idx;
int offset;
dev = (struct iowarrior *)file->private_data;
/* verify that the device wasn't unplugged */
if (dev == NULL || !dev->present)
return -ENODEV;
dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);
/* read count must be packet size (+ time stamp) */
if ((count != dev->report_size)
&& (count != (dev->report_size + 1)))
return -EINVAL;
/* repeat until no buffer overrun in callback handler occur */
do {
atomic_set(&dev->overflow_flag, 0);
if ((read_idx = read_index(dev)) == -1) {
/* queue emty */
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
else {
//next line will return when there is either new data, or the device is unplugged
int r = wait_event_interruptible(dev->read_wait,
(!dev->present
|| (read_idx =
read_index
(dev)) !=
-1));
if (r) {
//we were interrupted by a signal
return -ERESTART;
}
if (!dev->present) {
//The device was unplugged
return -ENODEV;
}
if (read_idx == -1) {
// Can this happen ???
return 0;
}
}
}
offset = read_idx * (dev->report_size + 1);
if (copy_to_user(buffer, dev->read_queue + offset, count)) {
return -EFAULT;
}
} while (atomic_read(&dev->overflow_flag));
read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx;
atomic_set(&dev->read_idx, read_idx);
return count;
}
/*
* iowarrior_write
*/
static ssize_t iowarrior_write(struct file *file,
const char __user *user_buffer,
size_t count, loff_t *ppos)
{
struct iowarrior *dev;
int retval = 0;
char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */
struct urb *int_out_urb = NULL;
dev = (struct iowarrior *)file->private_data;
mutex_lock(&dev->mutex);
/* verify that the device wasn't unplugged */
if (!dev->present) {
retval = -ENODEV;
goto exit;
}
dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);
/* if count is 0 we're already done */
if (count == 0) {
retval = 0;
goto exit;
}
/* We only accept full reports */
if (count != dev->report_size) {
retval = -EINVAL;
goto exit;
}
switch (dev->product_id) {
case USB_DEVICE_ID_CODEMERCS_IOW24:
case USB_DEVICE_ID_CODEMERCS_IOWPV1:
case USB_DEVICE_ID_CODEMERCS_IOWPV2:
case USB_DEVICE_ID_CODEMERCS_IOW40:
/* IOW24 and IOW40 use a synchronous call */
buf = kmalloc(count, GFP_KERNEL);
if (!buf) {
retval = -ENOMEM;
goto exit;
}
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
kfree(buf);
goto exit;
}
retval = usb_set_report(dev->interface, 2, 0, buf, count);
kfree(buf);
goto exit;
break;
case USB_DEVICE_ID_CODEMERCS_IOW56:
/* The IOW56 uses asynchronous IO and more urbs */
if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) {
/* Wait until we are below the limit for submitted urbs */
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
goto exit;
} else {
retval = wait_event_interruptible(dev->write_wait,
(!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT)));
if (retval) {
/* we were interrupted by a signal */
retval = -ERESTART;
goto exit;
}
if (!dev->present) {
/* The device was unplugged */
retval = -ENODEV;
goto exit;
}
if (!dev->opened) {
/* We were closed while waiting for an URB */
retval = -ENODEV;
goto exit;
}
}
}
atomic_inc(&dev->write_busy);
int_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!int_out_urb) {
retval = -ENOMEM;
dbg("%s Unable to allocate urb ", __func__);
goto error_no_urb;
}
buf = usb_buffer_alloc(dev->udev, dev->report_size,
GFP_KERNEL, &int_out_urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
dbg("%s Unable to allocate buffer ", __func__);
goto error_no_buffer;
}
usb_fill_int_urb(int_out_urb, dev->udev,
usb_sndintpipe(dev->udev,
dev->int_out_endpoint->bEndpointAddress),
buf, dev->report_size,
iowarrior_write_callback, dev,
dev->int_out_endpoint->bInterval);
int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}
retval = usb_submit_urb(int_out_urb, GFP_KERNEL);
if (retval) {
dbg("%s submit error %d for urb nr.%d", __func__,
retval, atomic_read(&dev->write_busy));
goto error;
}
/* submit was ok */
retval = count;
usb_free_urb(int_out_urb);
goto exit;
break;
default:
/* what do we have here ? An unsupported Product-ID ? */
dev_err(&dev->interface->dev, "%s - not supported for product=0x%x\n",
__func__, dev->product_id);
retval = -EFAULT;
goto exit;
break;
}
error:
usb_buffer_free(dev->udev, dev->report_size, buf,
int_out_urb->transfer_dma);
error_no_buffer:
usb_free_urb(int_out_urb);
error_no_urb:
atomic_dec(&dev->write_busy);
wake_up_interruptible(&dev->write_wait);
exit:
mutex_unlock(&dev->mutex);
return retval;
}
/**
* iowarrior_ioctl
*/
static long iowarrior_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct iowarrior *dev = NULL;
__u8 *buffer;
__u8 __user *user_buffer;
int retval;
int io_res; /* checks for bytes read/written and copy_to/from_user results */
dev = (struct iowarrior *)file->private_data;
if (dev == NULL) {
return -ENODEV;
}
buffer = kzalloc(dev->report_size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
/* lock this object */
lock_kernel();
mutex_lock(&dev->mutex);
/* verify that the device wasn't unplugged */
if (!dev->present) {
retval = -ENODEV;
goto error_out;
}
dbg("%s - minor %d, cmd 0x%.4x, arg %ld", __func__, dev->minor, cmd,
arg);
retval = 0;
io_res = 0;
switch (cmd) {
case IOW_WRITE:
if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 ||
dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 ||
dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 ||
dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) {
user_buffer = (__u8 __user *)arg;
io_res = copy_from_user(buffer, user_buffer,
dev->report_size);
if (io_res) {
retval = -EFAULT;
} else {
io_res = usb_set_report(dev->interface, 2, 0,
buffer,
dev->report_size);
if (io_res < 0)
retval = io_res;
}
} else {
retval = -EINVAL;
dev_err(&dev->interface->dev,
"ioctl 'IOW_WRITE' is not supported for product=0x%x.\n",
dev->product_id);
}
break;
case IOW_READ:
user_buffer = (__u8 __user *)arg;
io_res = usb_get_report(dev->udev,
dev->interface->cur_altsetting, 1, 0,
buffer, dev->report_size);
if (io_res < 0)
retval = io_res;
else {
io_res = copy_to_user(user_buffer, buffer, dev->report_size);
if (io_res < 0)
retval = -EFAULT;
}
break;
case IOW_GETINFO:
{
/* Report available information for the device */
struct iowarrior_info info;
/* needed for power consumption */
struct usb_config_descriptor *cfg_descriptor = &dev->udev->actconfig->desc;
memset(&info, 0, sizeof(info));
/* directly from the descriptor */
info.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
info.product = dev->product_id;
info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice);
/* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */
info.speed = le16_to_cpu(dev->udev->speed);
info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber;
info.report_size = dev->report_size;
/* serial number string has been read earlier 8 chars or empty string */
memcpy(info.serial, dev->chip_serial,
sizeof(dev->chip_serial));
if (cfg_descriptor == NULL) {
info.power = -1; /* no information available */
} else {
/* the MaxPower is stored in units of 2mA to make it fit into a byte-value */
info.power = cfg_descriptor->bMaxPower * 2;
}
io_res = copy_to_user((struct iowarrior_info __user *)arg, &info,
sizeof(struct iowarrior_info));
if (io_res < 0)
retval = -EFAULT;
break;
}
default:
/* return that we did not understand this ioctl call */
retval = -ENOTTY;
break;
}
error_out:
/* unlock the device */
mutex_unlock(&dev->mutex);
unlock_kernel();
kfree(buffer);
return retval;
}
/**
* iowarrior_open
*/
static int iowarrior_open(struct inode *inode, struct file *file)
{
struct iowarrior *dev = NULL;
struct usb_interface *interface;
int subminor;
int retval = 0;
dbg("%s", __func__);
subminor = iminor(inode);
interface = usb_find_interface(&iowarrior_driver, subminor);
if (!interface) {
err("%s - error, can't find device for minor %d", __func__,
subminor);
return -ENODEV;
}
mutex_lock(&iowarrior_open_disc_lock);
dev = usb_get_intfdata(interface);
if (!dev) {
mutex_unlock(&iowarrior_open_disc_lock);
return -ENODEV;
}
mutex_lock(&dev->mutex);
mutex_unlock(&iowarrior_open_disc_lock);
/* Only one process can open each device, no sharing. */
if (dev->opened) {
retval = -EBUSY;
goto out;
}
/* setup interrupt handler for receiving values */
if ((retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) < 0) {
dev_err(&interface->dev, "Error %d while submitting URB\n", retval);
retval = -EFAULT;
goto out;
}
/* increment our usage count for the driver */
++dev->opened;
/* save our object in the file's private structure */
file->private_data = dev;
retval = 0;
out:
mutex_unlock(&dev->mutex);
return retval;
}
/**
* iowarrior_release
*/
static int iowarrior_release(struct inode *inode, struct file *file)
{
struct iowarrior *dev;
int retval = 0;
dev = (struct iowarrior *)file->private_data;
if (dev == NULL) {
return -ENODEV;
}
dbg("%s - minor %d", __func__, dev->minor);
/* lock our device */
mutex_lock(&dev->mutex);
if (dev->opened <= 0) {
retval = -ENODEV; /* close called more than once */
mutex_unlock(&dev->mutex);
} else {
dev->opened = 0; /* we're closeing now */
retval = 0;
if (dev->present) {
/*
The device is still connected so we only shutdown
pending read-/write-ops.
*/
usb_kill_urb(dev->int_in_urb);
wake_up_interruptible(&dev->read_wait);
wake_up_interruptible(&dev->write_wait);
mutex_unlock(&dev->mutex);
} else {
/* The device was unplugged, cleanup resources */
mutex_unlock(&dev->mutex);
iowarrior_delete(dev);
}
}
return retval;
}
static unsigned iowarrior_poll(struct file *file, poll_table * wait)
{
struct iowarrior *dev = file->private_data;
unsigned int mask = 0;
if (!dev->present)
return POLLERR | POLLHUP;
poll_wait(file, &dev->read_wait, wait);
poll_wait(file, &dev->write_wait, wait);
if (!dev->present)
return POLLERR | POLLHUP;
if (read_index(dev) != -1)
mask |= POLLIN | POLLRDNORM;
if (atomic_read(&dev->write_busy) < MAX_WRITES_IN_FLIGHT)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
/*
* File operations needed when we register this driver.
* This assumes that this driver NEEDS file operations,
* of course, which means that the driver is expected
* to have a node in the /dev directory. If the USB
* device were for a network interface then the driver
* would use "struct net_driver" instead, and a serial
* device would use "struct tty_driver".
*/
static const struct file_operations iowarrior_fops = {
.owner = THIS_MODULE,
.write = iowarrior_write,
.read = iowarrior_read,
.unlocked_ioctl = iowarrior_ioctl,
.open = iowarrior_open,
.release = iowarrior_release,
.poll = iowarrior_poll,
};
static char *iowarrior_devnode(struct device *dev, mode_t *mode)
{
return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
}
/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with devfs and the driver core
*/
static struct usb_class_driver iowarrior_class = {
.name = "iowarrior%d",
.devnode = iowarrior_devnode,
.fops = &iowarrior_fops,
.minor_base = IOWARRIOR_MINOR_BASE,
};
/*---------------------------------*/
/* probe and disconnect functions */
/*---------------------------------*/
/**
* iowarrior_probe
*
* Called by the usb core when a new device is connected that it thinks
* this driver might be interested in.
*/
static int iowarrior_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct iowarrior *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int i;
int retval = -ENOMEM;
/* allocate memory for our device state and intialize it */
dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
return retval;
}
mutex_init(&dev->mutex);
atomic_set(&dev->intr_idx, 0);
atomic_set(&dev->read_idx, 0);
spin_lock_init(&dev->intr_idx_lock);
atomic_set(&dev->overflow_flag, 0);
init_waitqueue_head(&dev->read_wait);
atomic_set(&dev->write_busy, 0);
init_waitqueue_head(&dev->write_wait);
dev->udev = udev;
dev->interface = interface;
iface_desc = interface->cur_altsetting;
dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
/* set up the endpoint information */
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (usb_endpoint_is_int_in(endpoint))
dev->int_in_endpoint = endpoint;
if (usb_endpoint_is_int_out(endpoint))
/* this one will match for the IOWarrior56 only */
dev->int_out_endpoint = endpoint;
}
/* we have to check the report_size often, so remember it in the endianess suitable for our machine */
dev->report_size = le16_to_cpu(dev->int_in_endpoint->wMaxPacketSize);
if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
(dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56))
/* IOWarrior56 has wMaxPacketSize different from report size */
dev->report_size = 7;
/* create the urb and buffer for reading */
dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->int_in_urb) {
dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
goto error;
}
dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL);
if (!dev->int_in_buffer) {
dev_err(&interface->dev, "Couldn't allocate int_in_buffer\n");
goto error;
}
usb_fill_int_urb(dev->int_in_urb, dev->udev,
usb_rcvintpipe(dev->udev,
dev->int_in_endpoint->bEndpointAddress),
dev->int_in_buffer, dev->report_size,
iowarrior_callback, dev,
dev->int_in_endpoint->bInterval);
/* create an internal buffer for interrupt data from the device */
dev->read_queue =
kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER),
GFP_KERNEL);
if (!dev->read_queue) {
dev_err(&interface->dev, "Couldn't allocate read_queue\n");
goto error;
}
/* Get the serial-number of the chip */
memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial,
sizeof(dev->chip_serial));
if (strlen(dev->chip_serial) != 8)
memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
/* Set the idle timeout to 0, if this is interface 0 */
if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x0A,
USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
0, NULL, 0, USB_CTRL_SET_TIMEOUT);
}
/* allow device read and ioctl */
dev->present = 1;
/* we can register the device now, as it is ready */
usb_set_intfdata(interface, dev);
retval = usb_register_dev(interface, &iowarrior_class);
if (retval) {
/* something prevented us from registering this driver */
dev_err(&interface->dev, "Not able to get a minor for this device.\n");
usb_set_intfdata(interface, NULL);
goto error;
}
dev->minor = interface->minor;
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
"now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
return retval;
error:
iowarrior_delete(dev);
return retval;
}
/**
* iowarrior_disconnect
*
* Called by the usb core when the device is removed from the system.
*/
static void iowarrior_disconnect(struct usb_interface *interface)
{
struct iowarrior *dev;
int minor;
dev = usb_get_intfdata(interface);
mutex_lock(&iowarrior_open_disc_lock);
usb_set_intfdata(interface, NULL);
minor = dev->minor;
/* give back our minor */
usb_deregister_dev(interface, &iowarrior_class);
mutex_lock(&dev->mutex);
/* prevent device read, write and ioctl */
dev->present = 0;
mutex_unlock(&dev->mutex);
mutex_unlock(&iowarrior_open_disc_lock);
if (dev->opened) {
/* There is a process that holds a filedescriptor to the device ,
so we only shutdown read-/write-ops going on.
Deleting the device is postponed until close() was called.
*/
usb_kill_urb(dev->int_in_urb);
wake_up_interruptible(&dev->read_wait);
wake_up_interruptible(&dev->write_wait);
} else {
/* no process is using the device, cleanup now */
iowarrior_delete(dev);
}
dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
minor - IOWARRIOR_MINOR_BASE);
}
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver iowarrior_driver = {
.name = "iowarrior",
.probe = iowarrior_probe,
.disconnect = iowarrior_disconnect,
.id_table = iowarrior_ids,
};
static int __init iowarrior_init(void)
{
return usb_register(&iowarrior_driver);
}
static void __exit iowarrior_exit(void)
{
usb_deregister(&iowarrior_driver);
}
module_init(iowarrior_init);
module_exit(iowarrior_exit);

View File

@@ -0,0 +1,140 @@
/*
* Driver for loading USB isight firmware
*
* Copyright (C) 2008 Matthew Garrett <mjg@redhat.com>
*
* 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, version 2.
*
* The USB isight cameras in recent Apples are roughly compatible with the USB
* video class specification, and can be driven by uvcvideo. However, they
* need firmware to be loaded beforehand. After firmware loading, the device
* detaches from the USB bus and reattaches with a new device ID. It can then
* be claimed by the uvc driver.
*
* The firmware is non-free and must be extracted by the user. Tools to do this
* are available at http://bersace03.free.fr/ift/
*
* The isight firmware loading was reverse engineered by Johannes Berg
* <johannes@sipsolutions.de>, and this driver is based on code by Ronald
* Bultje <rbultje@ronald.bitfreak.net>
*/
#include <linux/usb.h>
#include <linux/firmware.h>
#include <linux/errno.h>
#include <linux/module.h>
static struct usb_device_id id_table[] = {
{USB_DEVICE(0x05ac, 0x8300)},
{},
};
MODULE_DEVICE_TABLE(usb, id_table);
static int isight_firmware_load(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
int llen, len, req, ret = 0;
const struct firmware *firmware;
unsigned char *buf = kmalloc(50, GFP_KERNEL);
unsigned char data[4];
const u8 *ptr;
if (!buf)
return -ENOMEM;
if (request_firmware(&firmware, "isight.fw", &dev->dev) != 0) {
printk(KERN_ERR "Unable to load isight firmware\n");
ret = -ENODEV;
goto out;
}
ptr = firmware->data;
if (usb_control_msg
(dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\1", 1,
300) != 1) {
printk(KERN_ERR
"Failed to initialise isight firmware loader\n");
ret = -ENODEV;
goto out;
}
while (ptr+4 <= firmware->data+firmware->size) {
memcpy(data, ptr, 4);
len = (data[0] << 8 | data[1]);
req = (data[2] << 8 | data[3]);
ptr += 4;
if (len == 0x8001)
break; /* success */
else if (len == 0)
continue;
for (; len > 0; req += 50) {
llen = min(len, 50);
len -= llen;
if (ptr+llen > firmware->data+firmware->size) {
printk(KERN_ERR
"Malformed isight firmware");
ret = -ENODEV;
goto out;
}
memcpy(buf, ptr, llen);
ptr += llen;
if (usb_control_msg
(dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, req, 0,
buf, llen, 300) != llen) {
printk(KERN_ERR
"Failed to load isight firmware\n");
ret = -ENODEV;
goto out;
}
}
}
if (usb_control_msg
(dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\0", 1,
300) != 1) {
printk(KERN_ERR "isight firmware loading completion failed\n");
ret = -ENODEV;
}
out:
kfree(buf);
release_firmware(firmware);
return ret;
}
static void isight_firmware_disconnect(struct usb_interface *intf)
{
}
static struct usb_driver isight_firmware_driver = {
.name = "isight_firmware",
.probe = isight_firmware_load,
.disconnect = isight_firmware_disconnect,
.id_table = id_table,
};
static int __init isight_firmware_init(void)
{
return usb_register(&isight_firmware_driver);
}
static void __exit isight_firmware_exit(void)
{
usb_deregister(&isight_firmware_driver);
}
module_init(isight_firmware_init);
module_exit(isight_firmware_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");

View File

@@ -0,0 +1,817 @@
/**
* Generic USB driver for report based interrupt in/out devices
* like LD Didactic's USB devices. LD Didactic's USB devices are
* HID devices which do not use HID report definitons (they use
* raw interrupt in and our reports only for communication).
*
* This driver uses a ring buffer for time critical reading of
* interrupt in reports and provides read and write methods for
* raw interrupt reports (similar to the Windows HID driver).
* Devices based on the book USB COMPLETE by Jan Axelson may need
* such a compatibility to the Windows HID driver.
*
* Copyright (C) 2005 Michael Hund <mhund@ld-didactic.de>
*
* 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.
*
* Derived from Lego USB Tower driver
* Copyright (C) 2003 David Glance <advidgsf@sourceforge.net>
* 2001-2004 Juergen Stuber <starblue@users.sourceforge.net>
*
* V0.1 (mh) Initial version
* V0.11 (mh) Added raw support for HID 1.0 devices (no interrupt out endpoint)
* V0.12 (mh) Added kmalloc check for string buffer
* V0.13 (mh) Added support for LD X-Ray and Machine Test System
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/poll.h>
/* Define these values to match your devices */
#define USB_VENDOR_ID_LD 0x0f11 /* USB Vendor ID of LD Didactic GmbH */
#define USB_DEVICE_ID_LD_CASSY 0x1000 /* USB Product ID of CASSY-S */
#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 /* USB Product ID of Pocket-CASSY */
#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 /* USB Product ID of Mobile-CASSY */
#define USB_DEVICE_ID_LD_JWM 0x1080 /* USB Product ID of Joule and Wattmeter */
#define USB_DEVICE_ID_LD_DMMP 0x1081 /* USB Product ID of Digital Multimeter P (reserved) */
#define USB_DEVICE_ID_LD_UMIP 0x1090 /* USB Product ID of UMI P */
#define USB_DEVICE_ID_LD_XRAY1 0x1100 /* USB Product ID of X-Ray Apparatus */
#define USB_DEVICE_ID_LD_XRAY2 0x1101 /* USB Product ID of X-Ray Apparatus */
#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 /* USB Product ID of VideoCom */
#define USB_DEVICE_ID_LD_COM3LAB 0x2000 /* USB Product ID of COM3LAB */
#define USB_DEVICE_ID_LD_TELEPORT 0x2010 /* USB Product ID of Terminal Adapter */
#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 /* USB Product ID of Network Analyser */
#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 /* USB Product ID of Converter Control Unit */
#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 /* USB Product ID of Machine Test System */
#define USB_VENDOR_ID_VERNIER 0x08f7
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define USB_LD_MINOR_BASE 0
#else
#define USB_LD_MINOR_BASE 176
#endif
/* table of devices that work with this driver */
static struct usb_device_id ld_usb_table [] = {
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) },
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
{ USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ld_usb_table);
MODULE_VERSION("V0.13");
MODULE_AUTHOR("Michael Hund <mhund@ld-didactic.de>");
MODULE_DESCRIPTION("LD USB Driver");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("LD USB Devices");
#ifdef CONFIG_USB_DEBUG
static int debug = 1;
#else
static int debug = 0;
#endif
/* Use our own dbg macro */
#define dbg_info(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
/* Module parameters */
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
/* All interrupt in transfers are collected in a ring buffer to
* avoid racing conditions and get better performance of the driver.
*/
static int ring_buffer_size = 128;
module_param(ring_buffer_size, int, 0);
MODULE_PARM_DESC(ring_buffer_size, "Read ring buffer size in reports");
/* The write_buffer can contain more than one interrupt out transfer.
*/
static int write_buffer_size = 10;
module_param(write_buffer_size, int, 0);
MODULE_PARM_DESC(write_buffer_size, "Write buffer size in reports");
/* As of kernel version 2.6.4 ehci-hcd uses an
* "only one interrupt transfer per frame" shortcut
* to simplify the scheduling of periodic transfers.
* This conflicts with our standard 1ms intervals for in and out URBs.
* We use default intervals of 2ms for in and 2ms for out transfers,
* which should be fast enough.
* Increase the interval to allow more devices that do interrupt transfers,
* or set to 1 to use the standard interval from the endpoint descriptors.
*/
static int min_interrupt_in_interval = 2;
module_param(min_interrupt_in_interval, int, 0);
MODULE_PARM_DESC(min_interrupt_in_interval, "Minimum interrupt in interval in ms");
static int min_interrupt_out_interval = 2;
module_param(min_interrupt_out_interval, int, 0);
MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in ms");
/* Structure to hold all of our device specific stuff */
struct ld_usb {
struct mutex mutex; /* locks this structure */
struct usb_interface* intf; /* save off the usb interface pointer */
int open_count; /* number of times this port has been opened */
char* ring_buffer;
unsigned int ring_head;
unsigned int ring_tail;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
char* interrupt_in_buffer;
struct usb_endpoint_descriptor* interrupt_in_endpoint;
struct urb* interrupt_in_urb;
int interrupt_in_interval;
size_t interrupt_in_endpoint_size;
int interrupt_in_running;
int interrupt_in_done;
int buffer_overflow;
spinlock_t rbsl;
char* interrupt_out_buffer;
struct usb_endpoint_descriptor* interrupt_out_endpoint;
struct urb* interrupt_out_urb;
int interrupt_out_interval;
size_t interrupt_out_endpoint_size;
int interrupt_out_busy;
};
static struct usb_driver ld_usb_driver;
/**
* ld_usb_abort_transfers
* aborts transfers and frees associated data structures
*/
static void ld_usb_abort_transfers(struct ld_usb *dev)
{
/* shutdown transfer */
if (dev->interrupt_in_running) {
dev->interrupt_in_running = 0;
if (dev->intf)
usb_kill_urb(dev->interrupt_in_urb);
}
if (dev->interrupt_out_busy)
if (dev->intf)
usb_kill_urb(dev->interrupt_out_urb);
}
/**
* ld_usb_delete
*/
static void ld_usb_delete(struct ld_usb *dev)
{
ld_usb_abort_transfers(dev);
/* free data structures */
usb_free_urb(dev->interrupt_in_urb);
usb_free_urb(dev->interrupt_out_urb);
kfree(dev->ring_buffer);
kfree(dev->interrupt_in_buffer);
kfree(dev->interrupt_out_buffer);
kfree(dev);
}
/**
* ld_usb_interrupt_in_callback
*/
static void ld_usb_interrupt_in_callback(struct urb *urb)
{
struct ld_usb *dev = urb->context;
size_t *actual_buffer;
unsigned int next_ring_head;
int status = urb->status;
int retval;
if (status) {
if (status == -ENOENT ||
status == -ECONNRESET ||
status == -ESHUTDOWN) {
goto exit;
} else {
dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n",
__func__, status);
spin_lock(&dev->rbsl);
goto resubmit; /* maybe we can recover */
}
}
spin_lock(&dev->rbsl);
if (urb->actual_length > 0) {
next_ring_head = (dev->ring_head+1) % ring_buffer_size;
if (next_ring_head != dev->ring_tail) {
actual_buffer = (size_t*)(dev->ring_buffer + dev->ring_head*(sizeof(size_t)+dev->interrupt_in_endpoint_size));
/* actual_buffer gets urb->actual_length + interrupt_in_buffer */
*actual_buffer = urb->actual_length;
memcpy(actual_buffer+1, dev->interrupt_in_buffer, urb->actual_length);
dev->ring_head = next_ring_head;
dbg_info(&dev->intf->dev, "%s: received %d bytes\n",
__func__, urb->actual_length);
} else {
dev_warn(&dev->intf->dev,
"Ring buffer overflow, %d bytes dropped\n",
urb->actual_length);
dev->buffer_overflow = 1;
}
}
resubmit:
/* resubmit if we're still running */
if (dev->interrupt_in_running && !dev->buffer_overflow && dev->intf) {
retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);
if (retval) {
dev_err(&dev->intf->dev,
"usb_submit_urb failed (%d)\n", retval);
dev->buffer_overflow = 1;
}
}
spin_unlock(&dev->rbsl);
exit:
dev->interrupt_in_done = 1;
wake_up_interruptible(&dev->read_wait);
}
/**
* ld_usb_interrupt_out_callback
*/
static void ld_usb_interrupt_out_callback(struct urb *urb)
{
struct ld_usb *dev = urb->context;
int status = urb->status;
/* sync/async unlink faults aren't errors */
if (status && !(status == -ENOENT ||
status == -ECONNRESET ||
status == -ESHUTDOWN))
dbg_info(&dev->intf->dev,
"%s - nonzero write interrupt status received: %d\n",
__func__, status);
dev->interrupt_out_busy = 0;
wake_up_interruptible(&dev->write_wait);
}
/**
* ld_usb_open
*/
static int ld_usb_open(struct inode *inode, struct file *file)
{
struct ld_usb *dev;
int subminor;
int retval;
struct usb_interface *interface;
nonseekable_open(inode, file);
subminor = iminor(inode);
interface = usb_find_interface(&ld_usb_driver, subminor);
if (!interface) {
err("%s - error, can't find device for minor %d\n",
__func__, subminor);
return -ENODEV;
}
dev = usb_get_intfdata(interface);
if (!dev)
return -ENODEV;
/* lock this device */
if (mutex_lock_interruptible(&dev->mutex))
return -ERESTARTSYS;
/* allow opening only once */
if (dev->open_count) {
retval = -EBUSY;
goto unlock_exit;
}
dev->open_count = 1;
/* initialize in direction */
dev->ring_head = 0;
dev->ring_tail = 0;
dev->buffer_overflow = 0;
usb_fill_int_urb(dev->interrupt_in_urb,
interface_to_usbdev(interface),
usb_rcvintpipe(interface_to_usbdev(interface),
dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
dev->interrupt_in_endpoint_size,
ld_usb_interrupt_in_callback,
dev,
dev->interrupt_in_interval);
dev->interrupt_in_running = 1;
dev->interrupt_in_done = 0;
retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
if (retval) {
dev_err(&interface->dev, "Couldn't submit interrupt_in_urb %d\n", retval);
dev->interrupt_in_running = 0;
dev->open_count = 0;
goto unlock_exit;
}
/* save device in the file's private structure */
file->private_data = dev;
unlock_exit:
mutex_unlock(&dev->mutex);
return retval;
}
/**
* ld_usb_release
*/
static int ld_usb_release(struct inode *inode, struct file *file)
{
struct ld_usb *dev;
int retval = 0;
dev = file->private_data;
if (dev == NULL) {
retval = -ENODEV;
goto exit;
}
if (mutex_lock_interruptible(&dev->mutex)) {
retval = -ERESTARTSYS;
goto exit;
}
if (dev->open_count != 1) {
retval = -ENODEV;
goto unlock_exit;
}
if (dev->intf == NULL) {
/* the device was unplugged before the file was released */
mutex_unlock(&dev->mutex);
/* unlock here as ld_usb_delete frees dev */
ld_usb_delete(dev);
goto exit;
}
/* wait until write transfer is finished */
if (dev->interrupt_out_busy)
wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ);
ld_usb_abort_transfers(dev);
dev->open_count = 0;
unlock_exit:
mutex_unlock(&dev->mutex);
exit:
return retval;
}
/**
* ld_usb_poll
*/
static unsigned int ld_usb_poll(struct file *file, poll_table *wait)
{
struct ld_usb *dev;
unsigned int mask = 0;
dev = file->private_data;
if (!dev->intf)
return POLLERR | POLLHUP;
poll_wait(file, &dev->read_wait, wait);
poll_wait(file, &dev->write_wait, wait);
if (dev->ring_head != dev->ring_tail)
mask |= POLLIN | POLLRDNORM;
if (!dev->interrupt_out_busy)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
/**
* ld_usb_read
*/
static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count,
loff_t *ppos)
{
struct ld_usb *dev;
size_t *actual_buffer;
size_t bytes_to_read;
int retval = 0;
int rv;
dev = file->private_data;
/* verify that we actually have some data to read */
if (count == 0)
goto exit;
/* lock this object */
if (mutex_lock_interruptible(&dev->mutex)) {
retval = -ERESTARTSYS;
goto exit;
}
/* verify that the device wasn't unplugged */
if (dev->intf == NULL) {
retval = -ENODEV;
err("No device or device unplugged %d\n", retval);
goto unlock_exit;
}
/* wait for data */
spin_lock_irq(&dev->rbsl);
if (dev->ring_head == dev->ring_tail) {
dev->interrupt_in_done = 0;
spin_unlock_irq(&dev->rbsl);
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
goto unlock_exit;
}
retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done);
if (retval < 0)
goto unlock_exit;
} else {
spin_unlock_irq(&dev->rbsl);
}
/* actual_buffer contains actual_length + interrupt_in_buffer */
actual_buffer = (size_t*)(dev->ring_buffer + dev->ring_tail*(sizeof(size_t)+dev->interrupt_in_endpoint_size));
bytes_to_read = min(count, *actual_buffer);
if (bytes_to_read < *actual_buffer)
dev_warn(&dev->intf->dev, "Read buffer overflow, %zd bytes dropped\n",
*actual_buffer-bytes_to_read);
/* copy one interrupt_in_buffer from ring_buffer into userspace */
if (copy_to_user(buffer, actual_buffer+1, bytes_to_read)) {
retval = -EFAULT;
goto unlock_exit;
}
dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size;
retval = bytes_to_read;
spin_lock_irq(&dev->rbsl);
if (dev->buffer_overflow) {
dev->buffer_overflow = 0;
spin_unlock_irq(&dev->rbsl);
rv = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
if (rv < 0)
dev->buffer_overflow = 1;
} else {
spin_unlock_irq(&dev->rbsl);
}
unlock_exit:
/* unlock the device */
mutex_unlock(&dev->mutex);
exit:
return retval;
}
/**
* ld_usb_write
*/
static ssize_t ld_usb_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct ld_usb *dev;
size_t bytes_to_write;
int retval = 0;
dev = file->private_data;
/* verify that we actually have some data to write */
if (count == 0)
goto exit;
/* lock this object */
if (mutex_lock_interruptible(&dev->mutex)) {
retval = -ERESTARTSYS;
goto exit;
}
/* verify that the device wasn't unplugged */
if (dev->intf == NULL) {
retval = -ENODEV;
err("No device or device unplugged %d\n", retval);
goto unlock_exit;
}
/* wait until previous transfer is finished */
if (dev->interrupt_out_busy) {
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
goto unlock_exit;
}
retval = wait_event_interruptible(dev->write_wait, !dev->interrupt_out_busy);
if (retval < 0) {
goto unlock_exit;
}
}
/* write the data into interrupt_out_buffer from userspace */
bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size);
if (bytes_to_write < count)
dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write);
dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __func__, count, bytes_to_write);
if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) {
retval = -EFAULT;
goto unlock_exit;
}
if (dev->interrupt_out_endpoint == NULL) {
/* try HID_REQ_SET_REPORT=9 on control_endpoint instead of interrupt_out_endpoint */
retval = usb_control_msg(interface_to_usbdev(dev->intf),
usb_sndctrlpipe(interface_to_usbdev(dev->intf), 0),
9,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
1 << 8, 0,
dev->interrupt_out_buffer,
bytes_to_write,
USB_CTRL_SET_TIMEOUT * HZ);
if (retval < 0)
err("Couldn't submit HID_REQ_SET_REPORT %d\n", retval);
goto unlock_exit;
}
/* send off the urb */
usb_fill_int_urb(dev->interrupt_out_urb,
interface_to_usbdev(dev->intf),
usb_sndintpipe(interface_to_usbdev(dev->intf),
dev->interrupt_out_endpoint->bEndpointAddress),
dev->interrupt_out_buffer,
bytes_to_write,
ld_usb_interrupt_out_callback,
dev,
dev->interrupt_out_interval);
dev->interrupt_out_busy = 1;
wmb();
retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
if (retval) {
dev->interrupt_out_busy = 0;
err("Couldn't submit interrupt_out_urb %d\n", retval);
goto unlock_exit;
}
retval = bytes_to_write;
unlock_exit:
/* unlock the device */
mutex_unlock(&dev->mutex);
exit:
return retval;
}
/* file operations needed when we register this driver */
static const struct file_operations ld_usb_fops = {
.owner = THIS_MODULE,
.read = ld_usb_read,
.write = ld_usb_write,
.open = ld_usb_open,
.release = ld_usb_release,
.poll = ld_usb_poll,
};
/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with the driver core
*/
static struct usb_class_driver ld_usb_class = {
.name = "ldusb%d",
.fops = &ld_usb_fops,
.minor_base = USB_LD_MINOR_BASE,
};
/**
* ld_usb_probe
*
* Called by the usb core when a new device is connected that it thinks
* this driver might be interested in.
*/
static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct ld_usb *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
char *buffer;
int i;
int retval = -ENOMEM;
/* allocate memory for our device state and intialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&intf->dev, "Out of memory\n");
goto exit;
}
mutex_init(&dev->mutex);
spin_lock_init(&dev->rbsl);
dev->intf = intf;
init_waitqueue_head(&dev->read_wait);
init_waitqueue_head(&dev->write_wait);
/* workaround for early firmware versions on fast computers */
if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VENDOR_ID_LD) &&
((le16_to_cpu(udev->descriptor.idProduct) == USB_DEVICE_ID_LD_CASSY) ||
(le16_to_cpu(udev->descriptor.idProduct) == USB_DEVICE_ID_LD_COM3LAB)) &&
(le16_to_cpu(udev->descriptor.bcdDevice) <= 0x103)) {
buffer = kmalloc(256, GFP_KERNEL);
if (buffer == NULL) {
dev_err(&intf->dev, "Couldn't allocate string buffer\n");
goto error;
}
/* usb_string makes SETUP+STALL to leave always ControlReadLoop */
usb_string(udev, 255, buffer, 256);
kfree(buffer);
}
iface_desc = intf->cur_altsetting;
/* set up the endpoint information */
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (usb_endpoint_is_int_in(endpoint))
dev->interrupt_in_endpoint = endpoint;
if (usb_endpoint_is_int_out(endpoint))
dev->interrupt_out_endpoint = endpoint;
}
if (dev->interrupt_in_endpoint == NULL) {
dev_err(&intf->dev, "Interrupt in endpoint not found\n");
goto error;
}
if (dev->interrupt_out_endpoint == NULL)
dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n");
dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);
dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL);
if (!dev->ring_buffer) {
dev_err(&intf->dev, "Couldn't allocate ring_buffer\n");
goto error;
}
dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL);
if (!dev->interrupt_in_buffer) {
dev_err(&intf->dev, "Couldn't allocate interrupt_in_buffer\n");
goto error;
}
dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->interrupt_in_urb) {
dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n");
goto error;
}
dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) :
udev->descriptor.bMaxPacketSize0;
dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL);
if (!dev->interrupt_out_buffer) {
dev_err(&intf->dev, "Couldn't allocate interrupt_out_buffer\n");
goto error;
}
dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->interrupt_out_urb) {
dev_err(&intf->dev, "Couldn't allocate interrupt_out_urb\n");
goto error;
}
dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint->bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
if (dev->interrupt_out_endpoint)
dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint->bInterval ? min_interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
/* we can register the device now, as it is ready */
usb_set_intfdata(intf, dev);
retval = usb_register_dev(intf, &ld_usb_class);
if (retval) {
/* something prevented us from registering this driver */
dev_err(&intf->dev, "Not able to get a minor for this device.\n");
usb_set_intfdata(intf, NULL);
goto error;
}
/* let the user know what node this device is now attached to */
dev_info(&intf->dev, "LD USB Device #%d now attached to major %d minor %d\n",
(intf->minor - USB_LD_MINOR_BASE), USB_MAJOR, intf->minor);
exit:
return retval;
error:
ld_usb_delete(dev);
return retval;
}
/**
* ld_usb_disconnect
*
* Called by the usb core when the device is removed from the system.
*/
static void ld_usb_disconnect(struct usb_interface *intf)
{
struct ld_usb *dev;
int minor;
dev = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
minor = intf->minor;
/* give back our minor */
usb_deregister_dev(intf, &ld_usb_class);
mutex_lock(&dev->mutex);
/* if the device is not opened, then we clean up right now */
if (!dev->open_count) {
mutex_unlock(&dev->mutex);
ld_usb_delete(dev);
} else {
dev->intf = NULL;
/* wake up pollers */
wake_up_interruptible_all(&dev->read_wait);
wake_up_interruptible_all(&dev->write_wait);
mutex_unlock(&dev->mutex);
}
dev_info(&intf->dev, "LD USB Device #%d now disconnected\n",
(minor - USB_LD_MINOR_BASE));
}
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver ld_usb_driver = {
.name = "ldusb",
.probe = ld_usb_probe,
.disconnect = ld_usb_disconnect,
.id_table = ld_usb_table,
};
/**
* ld_usb_init
*/
static int __init ld_usb_init(void)
{
int retval;
/* register this driver with the USB subsystem */
retval = usb_register(&ld_usb_driver);
if (retval)
err("usb_register failed for the "__FILE__" driver. Error number %d\n", retval);
return retval;
}
/**
* ld_usb_exit
*/
static void __exit ld_usb_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&ld_usb_driver);
}
module_init(ld_usb_init);
module_exit(ld_usb_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,558 @@
/* -*- linux-c -*- */
/*
* Driver for USB Rio 500
*
* Cesar Miquel (miquel@df.uba.ar)
*
* based on hp_scanner.c by David E. Nelson (dnelson@jump.net)
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
*
* Changelog:
* 30/05/2003 replaced lock/unlock kernel with up/down
* Daniele Bellucci bellucda@tiscali.it
* */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include "rio500_usb.h"
/*
* Version Information
*/
#define DRIVER_VERSION "v1.1"
#define DRIVER_AUTHOR "Cesar Miquel <miquel@df.uba.ar>"
#define DRIVER_DESC "USB Rio 500 driver"
#define RIO_MINOR 64
/* stall/wait timeout for rio */
#define NAK_TIMEOUT (HZ)
#define IBUF_SIZE 0x1000
/* Size of the rio buffer */
#define OBUF_SIZE 0x10000
struct rio_usb_data {
struct usb_device *rio_dev; /* init: probe_rio */
unsigned int ifnum; /* Interface number of the USB device */
int isopen; /* nz if open */
int present; /* Device is present on the bus */
char *obuf, *ibuf; /* transfer buffers */
char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */
wait_queue_head_t wait_q; /* for timeouts */
struct mutex lock; /* general race avoidance */
};
static struct rio_usb_data rio_instance;
static int open_rio(struct inode *inode, struct file *file)
{
struct rio_usb_data *rio = &rio_instance;
mutex_lock(&(rio->lock));
if (rio->isopen || !rio->present) {
mutex_unlock(&(rio->lock));
return -EBUSY;
}
rio->isopen = 1;
init_waitqueue_head(&rio->wait_q);
mutex_unlock(&(rio->lock));
dev_info(&rio->rio_dev->dev, "Rio opened.\n");
return 0;
}
static int close_rio(struct inode *inode, struct file *file)
{
struct rio_usb_data *rio = &rio_instance;
rio->isopen = 0;
dev_info(&rio->rio_dev->dev, "Rio closed.\n");
return 0;
}
static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg)
{
struct RioCommand rio_cmd;
struct rio_usb_data *rio = &rio_instance;
void __user *data;
unsigned char *buffer;
int result, requesttype;
int retries;
int retval=0;
lock_kernel();
mutex_lock(&(rio->lock));
/* Sanity check to make sure rio is connected, powered, etc */
if (rio->present == 0 || rio->rio_dev == NULL) {
retval = -ENODEV;
goto err_out;
}
switch (cmd) {
case RIO_RECV_COMMAND:
data = (void __user *) arg;
if (data == NULL)
break;
if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand))) {
retval = -EFAULT;
goto err_out;
}
if (rio_cmd.length < 0 || rio_cmd.length > PAGE_SIZE) {
retval = -EINVAL;
goto err_out;
}
buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
if (buffer == NULL) {
retval = -ENOMEM;
goto err_out;
}
if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length)) {
retval = -EFAULT;
free_page((unsigned long) buffer);
goto err_out;
}
requesttype = rio_cmd.requesttype | USB_DIR_IN |
USB_TYPE_VENDOR | USB_RECIP_DEVICE;
dbg
("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x",
requesttype, rio_cmd.request, rio_cmd.value,
rio_cmd.index, rio_cmd.length);
/* Send rio control message */
retries = 3;
while (retries) {
result = usb_control_msg(rio->rio_dev,
usb_rcvctrlpipe(rio-> rio_dev, 0),
rio_cmd.request,
requesttype,
rio_cmd.value,
rio_cmd.index, buffer,
rio_cmd.length,
jiffies_to_msecs(rio_cmd.timeout));
if (result == -ETIMEDOUT)
retries--;
else if (result < 0) {
err("Error executing ioctrl. code = %d", result);
retries = 0;
} else {
dbg("Executed ioctl. Result = %d (data=%02x)",
result, buffer[0]);
if (copy_to_user(rio_cmd.buffer, buffer,
rio_cmd.length)) {
free_page((unsigned long) buffer);
retval = -EFAULT;
goto err_out;
}
retries = 0;
}
/* rio_cmd.buffer contains a raw stream of single byte
data which has been returned from rio. Data is
interpreted at application level. For data that
will be cast to data types longer than 1 byte, data
will be little_endian and will potentially need to
be swapped at the app level */
}
free_page((unsigned long) buffer);
break;
case RIO_SEND_COMMAND:
data = (void __user *) arg;
if (data == NULL)
break;
if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand))) {
retval = -EFAULT;
goto err_out;
}
if (rio_cmd.length < 0 || rio_cmd.length > PAGE_SIZE) {
retval = -EINVAL;
goto err_out;
}
buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
if (buffer == NULL) {
retval = -ENOMEM;
goto err_out;
}
if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length)) {
free_page((unsigned long)buffer);
retval = -EFAULT;
goto err_out;
}
requesttype = rio_cmd.requesttype | USB_DIR_OUT |
USB_TYPE_VENDOR | USB_RECIP_DEVICE;
dbg("sending command: reqtype=%0x req=%0x value=%0x index=%0x len=%0x",
requesttype, rio_cmd.request, rio_cmd.value,
rio_cmd.index, rio_cmd.length);
/* Send rio control message */
retries = 3;
while (retries) {
result = usb_control_msg(rio->rio_dev,
usb_sndctrlpipe(rio-> rio_dev, 0),
rio_cmd.request,
requesttype,
rio_cmd.value,
rio_cmd.index, buffer,
rio_cmd.length,
jiffies_to_msecs(rio_cmd.timeout));
if (result == -ETIMEDOUT)
retries--;
else if (result < 0) {
err("Error executing ioctrl. code = %d", result);
retries = 0;
} else {
dbg("Executed ioctl. Result = %d", result);
retries = 0;
}
}
free_page((unsigned long) buffer);
break;
default:
retval = -ENOTTY;
break;
}
err_out:
mutex_unlock(&(rio->lock));
unlock_kernel();
return retval;
}
static ssize_t
write_rio(struct file *file, const char __user *buffer,
size_t count, loff_t * ppos)
{
DEFINE_WAIT(wait);
struct rio_usb_data *rio = &rio_instance;
unsigned long copy_size;
unsigned long bytes_written = 0;
unsigned int partial;
int result = 0;
int maxretry;
int errn = 0;
int intr;
intr = mutex_lock_interruptible(&(rio->lock));
if (intr)
return -EINTR;
/* Sanity check to make sure rio is connected, powered, etc */
if (rio->present == 0 || rio->rio_dev == NULL) {
mutex_unlock(&(rio->lock));
return -ENODEV;
}
do {
unsigned long thistime;
char *obuf = rio->obuf;
thistime = copy_size =
(count >= OBUF_SIZE) ? OBUF_SIZE : count;
if (copy_from_user(rio->obuf, buffer, copy_size)) {
errn = -EFAULT;
goto error;
}
maxretry = 5;
while (thistime) {
if (!rio->rio_dev) {
errn = -ENODEV;
goto error;
}
if (signal_pending(current)) {
mutex_unlock(&(rio->lock));
return bytes_written ? bytes_written : -EINTR;
}
result = usb_bulk_msg(rio->rio_dev,
usb_sndbulkpipe(rio->rio_dev, 2),
obuf, thistime, &partial, 5000);
dbg("write stats: result:%d thistime:%lu partial:%u",
result, thistime, partial);
if (result == -ETIMEDOUT) { /* NAK - so hold for a while */
if (!maxretry--) {
errn = -ETIME;
goto error;
}
prepare_to_wait(&rio->wait_q, &wait, TASK_INTERRUPTIBLE);
schedule_timeout(NAK_TIMEOUT);
finish_wait(&rio->wait_q, &wait);
continue;
} else if (!result && partial) {
obuf += partial;
thistime -= partial;
} else
break;
};
if (result) {
err("Write Whoops - %x", result);
errn = -EIO;
goto error;
}
bytes_written += copy_size;
count -= copy_size;
buffer += copy_size;
} while (count > 0);
mutex_unlock(&(rio->lock));
return bytes_written ? bytes_written : -EIO;
error:
mutex_unlock(&(rio->lock));
return errn;
}
static ssize_t
read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
{
DEFINE_WAIT(wait);
struct rio_usb_data *rio = &rio_instance;
ssize_t read_count;
unsigned int partial;
int this_read;
int result;
int maxretry = 10;
char *ibuf;
int intr;
intr = mutex_lock_interruptible(&(rio->lock));
if (intr)
return -EINTR;
/* Sanity check to make sure rio is connected, powered, etc */
if (rio->present == 0 || rio->rio_dev == NULL) {
mutex_unlock(&(rio->lock));
return -ENODEV;
}
ibuf = rio->ibuf;
read_count = 0;
while (count > 0) {
if (signal_pending(current)) {
mutex_unlock(&(rio->lock));
return read_count ? read_count : -EINTR;
}
if (!rio->rio_dev) {
mutex_unlock(&(rio->lock));
return -ENODEV;
}
this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
result = usb_bulk_msg(rio->rio_dev,
usb_rcvbulkpipe(rio->rio_dev, 1),
ibuf, this_read, &partial,
8000);
dbg("read stats: result:%d this_read:%u partial:%u",
result, this_read, partial);
if (partial) {
count = this_read = partial;
} else if (result == -ETIMEDOUT || result == 15) { /* FIXME: 15 ??? */
if (!maxretry--) {
mutex_unlock(&(rio->lock));
err("read_rio: maxretry timeout");
return -ETIME;
}
prepare_to_wait(&rio->wait_q, &wait, TASK_INTERRUPTIBLE);
schedule_timeout(NAK_TIMEOUT);
finish_wait(&rio->wait_q, &wait);
continue;
} else if (result != -EREMOTEIO) {
mutex_unlock(&(rio->lock));
err("Read Whoops - result:%u partial:%u this_read:%u",
result, partial, this_read);
return -EIO;
} else {
mutex_unlock(&(rio->lock));
return (0);
}
if (this_read) {
if (copy_to_user(buffer, ibuf, this_read)) {
mutex_unlock(&(rio->lock));
return -EFAULT;
}
count -= this_read;
read_count += this_read;
buffer += this_read;
}
}
mutex_unlock(&(rio->lock));
return read_count;
}
static const struct file_operations usb_rio_fops = {
.owner = THIS_MODULE,
.read = read_rio,
.write = write_rio,
.unlocked_ioctl = ioctl_rio,
.open = open_rio,
.release = close_rio,
};
static struct usb_class_driver usb_rio_class = {
.name = "rio500%d",
.fops = &usb_rio_fops,
.minor_base = RIO_MINOR,
};
static int probe_rio(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct rio_usb_data *rio = &rio_instance;
int retval;
dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);
retval = usb_register_dev(intf, &usb_rio_class);
if (retval) {
err("Not able to get a minor for this device.");
return -ENOMEM;
}
rio->rio_dev = dev;
if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) {
err("probe_rio: Not enough memory for the output buffer");
usb_deregister_dev(intf, &usb_rio_class);
return -ENOMEM;
}
dbg("probe_rio: obuf address:%p", rio->obuf);
if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) {
err("probe_rio: Not enough memory for the input buffer");
usb_deregister_dev(intf, &usb_rio_class);
kfree(rio->obuf);
return -ENOMEM;
}
dbg("probe_rio: ibuf address:%p", rio->ibuf);
mutex_init(&(rio->lock));
usb_set_intfdata (intf, rio);
rio->present = 1;
return 0;
}
static void disconnect_rio(struct usb_interface *intf)
{
struct rio_usb_data *rio = usb_get_intfdata (intf);
usb_set_intfdata (intf, NULL);
if (rio) {
usb_deregister_dev(intf, &usb_rio_class);
mutex_lock(&(rio->lock));
if (rio->isopen) {
rio->isopen = 0;
/* better let it finish - the release will do whats needed */
rio->rio_dev = NULL;
mutex_unlock(&(rio->lock));
return;
}
kfree(rio->ibuf);
kfree(rio->obuf);
dev_info(&intf->dev, "USB Rio disconnected.\n");
rio->present = 0;
mutex_unlock(&(rio->lock));
}
}
static struct usb_device_id rio_table [] = {
{ USB_DEVICE(0x0841, 1) }, /* Rio 500 */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, rio_table);
static struct usb_driver rio_driver = {
.name = "rio500",
.probe = probe_rio,
.disconnect = disconnect_rio,
.id_table = rio_table,
};
static int __init usb_rio_init(void)
{
int retval;
retval = usb_register(&rio_driver);
if (retval)
goto out;
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
out:
return retval;
}
static void __exit usb_rio_cleanup(void)
{
struct rio_usb_data *rio = &rio_instance;
rio->present = 0;
usb_deregister(&rio_driver);
}
module_init(usb_rio_init);
module_exit(usb_rio_cleanup);
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,37 @@
/* ----------------------------------------------------------------------
Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar)
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., 675 Mass Ave, Cambridge, MA 02139, USA.
---------------------------------------------------------------------- */
#define RIO_SEND_COMMAND 0x1
#define RIO_RECV_COMMAND 0x2
#define RIO_DIR_OUT 0x0
#define RIO_DIR_IN 0x1
struct RioCommand {
short length;
int request;
int requesttype;
int value;
int index;
void __user *buffer;
int timeout;
};

View File

@@ -0,0 +1,46 @@
config USB_SISUSBVGA
tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)"
depends on USB && (USB_MUSB_HDRC || USB_EHCI_HCD)
---help---
Say Y here if you intend to attach a USB2VGA dongle based on a
Net2280 and a SiS315 chip.
Note that this device requires a USB 2.0 host controller. It will not
work with USB 1.x controllers.
To compile this driver as a module, choose M here; the module will be
called sisusbvga. If unsure, say N.
config USB_SISUSBVGA_CON
bool "Text console and mode switching support" if USB_SISUSBVGA
depends on VT
select FONT_8x16
---help---
Say Y here if you want a VGA text console via the USB dongle or
want to support userland applications that utilize the driver's
display mode switching capabilities.
Note that this console supports VGA/EGA text mode only.
By default, the console part of the driver will not kick in when
the driver is initialized. If you want the driver to take over
one or more of the consoles, you need to specify the number of
the first and last consoles (starting at 1) as driver parameters.
For example, if the driver is compiled as a module:
modprobe sisusbvga first=1 last=5
If you use hotplug, add this to your modutils config files with
the "options" keyword, such as eg.
options sisusbvga first=1 last=5
If the driver is compiled into the kernel image, the parameters
must be given in the kernel command like, such as
sisusbvga.first=1 sisusbvga.last=5

View File

@@ -0,0 +1,8 @@
#
# Makefile for the sisusb driver (if driver is inside kernel tree).
#
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga.o
sisusbvga-objs := sisusb.o sisusb_init.o sisusb_con.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,311 @@
/*
* sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles
*
* Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
*
* If distributed as part of the Linux kernel, this code is licensed under the
* terms of the GPL v2.
*
* Otherwise, the following license terms apply:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1) Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2) Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3) The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Thomas Winischhofer <thomas@winischhofer.net>
*
*/
#ifndef _SISUSB_H_
#define _SISUSB_H_
#ifdef CONFIG_COMPAT
#define SISUSB_NEW_CONFIG_COMPAT
#endif
#include <linux/mutex.h>
/* For older kernels, support for text consoles is by default
* off. To enable text console support, change the following:
*/
/* #define CONFIG_USB_SISUSBVGA_CON */
/* Version Information */
#define SISUSB_VERSION 0
#define SISUSB_REVISION 0
#define SISUSB_PATCHLEVEL 8
/* Include console and mode switching code? */
#ifdef CONFIG_USB_SISUSBVGA_CON
#define INCL_SISUSB_CON 1
#endif
#include <linux/console.h>
#include <linux/vt_kern.h>
#include "sisusb_struct.h"
/* USB related */
#define SISUSB_MINOR 133 /* official */
/* Size of the sisusb input/output buffers */
#define SISUSB_IBUF_SIZE 0x01000
#define SISUSB_OBUF_SIZE 0x10000 /* fixed */
#define NUMOBUFS 8 /* max number of output buffers/output URBs */
/* About endianness:
*
* 1) I/O ports, PCI config registers. The read/write()
* calls emulate inX/outX. Hence, the data is
* expected/delivered in machine endiannes by this
* driver.
* 2) Video memory. The data is copied 1:1. There is
* no swapping. Ever. This means for userland that
* the data has to be prepared properly. (Hint:
* think graphics data format, command queue,
* hardware cursor.)
* 3) MMIO. Data is copied 1:1. MMIO must be swapped
* properly by userland.
*
*/
#ifdef __BIG_ENDIAN
#define SISUSB_CORRECT_ENDIANNESS_PACKET(p) \
do { \
p->header = cpu_to_le16(p->header); \
p->address = cpu_to_le32(p->address); \
p->data = cpu_to_le32(p->data); \
} while(0)
#else
#define SISUSB_CORRECT_ENDIANNESS_PACKET(p)
#endif
struct sisusb_usb_data;
struct sisusb_urb_context { /* urb->context for outbound bulk URBs */
struct sisusb_usb_data *sisusb;
int urbindex;
int *actual_length;
};
struct sisusb_usb_data {
struct usb_device *sisusb_dev;
struct usb_interface *interface;
struct kref kref;
wait_queue_head_t wait_q; /* for syncind and timeouts */
struct mutex lock; /* general race avoidance */
unsigned int ifnum; /* interface number of the USB device */
int minor; /* minor (for logging clarity) */
int isopen; /* !=0 if open */
int present; /* !=0 if device is present on the bus */
int ready; /* !=0 if device is ready for userland */
int numobufs; /* number of obufs = number of out urbs */
char *obuf[NUMOBUFS], *ibuf; /* transfer buffers */
int obufsize, ibufsize;
struct urb *sisurbout[NUMOBUFS];
struct urb *sisurbin;
unsigned char urbstatus[NUMOBUFS];
unsigned char completein;
struct sisusb_urb_context urbout_context[NUMOBUFS];
unsigned long flagb0;
unsigned long vrambase; /* framebuffer base */
unsigned int vramsize; /* framebuffer size (bytes) */
unsigned long mmiobase;
unsigned int mmiosize;
unsigned long ioportbase;
unsigned char devinit; /* device initialized? */
unsigned char gfxinit; /* graphics core initialized? */
unsigned short chipid, chipvendor;
unsigned short chiprevision;
#ifdef INCL_SISUSB_CON
struct SiS_Private *SiS_Pr;
unsigned long scrbuf;
unsigned int scrbuf_size;
int haveconsole, con_first, con_last;
int havethisconsole[MAX_NR_CONSOLES];
int textmodedestroyed;
unsigned int sisusb_num_columns; /* real number, not vt's idea */
int cur_start_addr, con_rolled_over;
int sisusb_cursor_loc, bad_cursor_pos;
int sisusb_cursor_size_from;
int sisusb_cursor_size_to;
int current_font_height, current_font_512;
int font_backup_size, font_backup_height, font_backup_512;
char *font_backup;
int font_slot;
struct vc_data *sisusb_display_fg;
int is_gfx;
int con_blanked;
#endif
};
#define to_sisusb_dev(d) container_of(d, struct sisusb_usb_data, kref)
/* USB transport related */
/* urbstatus */
#define SU_URB_BUSY 1
#define SU_URB_ALLOC 2
/* Endpoints */
#define SISUSB_EP_GFX_IN 0x0e /* gfx std packet out(0e)/in(8e) */
#define SISUSB_EP_GFX_OUT 0x0e
#define SISUSB_EP_GFX_BULK_OUT 0x01 /* gfx mem bulk out/in */
#define SISUSB_EP_GFX_BULK_IN 0x02 /* ? 2 is "OUT" ? */
#define SISUSB_EP_GFX_LBULK_OUT 0x03 /* gfx large mem bulk out */
#define SISUSB_EP_UNKNOWN_04 0x04 /* ? 4 is "OUT" ? - unused */
#define SISUSB_EP_BRIDGE_IN 0x0d /* Net2280 out(0d)/in(8d) */
#define SISUSB_EP_BRIDGE_OUT 0x0d
#define SISUSB_TYPE_MEM 0
#define SISUSB_TYPE_IO 1
struct sisusb_packet {
unsigned short header;
u32 address;
u32 data;
} __attribute__ ((__packed__));
#define CLEARPACKET(packet) memset(packet, 0, 10)
/* PCI bridge related */
#define SISUSB_PCI_MEMBASE 0xd0000000
#define SISUSB_PCI_MMIOBASE 0xe4000000
#define SISUSB_PCI_IOPORTBASE 0x0000d000
#define SISUSB_PCI_PSEUDO_MEMBASE 0x10000000
#define SISUSB_PCI_PSEUDO_MMIOBASE 0x20000000
#define SISUSB_PCI_PSEUDO_IOPORTBASE 0x0000d000
#define SISUSB_PCI_PSEUDO_PCIBASE 0x00010000
#define SISUSB_PCI_MMIOSIZE (128*1024)
#define SISUSB_PCI_PCONFSIZE 0x5c
/* graphics core related */
#define AROFFSET 0x40
#define ARROFFSET 0x41
#define GROFFSET 0x4e
#define SROFFSET 0x44
#define CROFFSET 0x54
#define MISCROFFSET 0x4c
#define MISCWOFFSET 0x42
#define INPUTSTATOFFSET 0x5A
#define PART1OFFSET 0x04
#define PART2OFFSET 0x10
#define PART3OFFSET 0x12
#define PART4OFFSET 0x14
#define PART5OFFSET 0x16
#define CAPTUREOFFSET 0x00
#define VIDEOOFFSET 0x02
#define COLREGOFFSET 0x48
#define PELMASKOFFSET 0x46
#define VGAENABLE 0x43
#define SISAR SISUSB_PCI_IOPORTBASE + AROFFSET
#define SISARR SISUSB_PCI_IOPORTBASE + ARROFFSET
#define SISGR SISUSB_PCI_IOPORTBASE + GROFFSET
#define SISSR SISUSB_PCI_IOPORTBASE + SROFFSET
#define SISCR SISUSB_PCI_IOPORTBASE + CROFFSET
#define SISMISCR SISUSB_PCI_IOPORTBASE + MISCROFFSET
#define SISMISCW SISUSB_PCI_IOPORTBASE + MISCWOFFSET
#define SISINPSTAT SISUSB_PCI_IOPORTBASE + INPUTSTATOFFSET
#define SISPART1 SISUSB_PCI_IOPORTBASE + PART1OFFSET
#define SISPART2 SISUSB_PCI_IOPORTBASE + PART2OFFSET
#define SISPART3 SISUSB_PCI_IOPORTBASE + PART3OFFSET
#define SISPART4 SISUSB_PCI_IOPORTBASE + PART4OFFSET
#define SISPART5 SISUSB_PCI_IOPORTBASE + PART5OFFSET
#define SISCAP SISUSB_PCI_IOPORTBASE + CAPTUREOFFSET
#define SISVID SISUSB_PCI_IOPORTBASE + VIDEOOFFSET
#define SISCOLIDXR SISUSB_PCI_IOPORTBASE + COLREGOFFSET - 1
#define SISCOLIDX SISUSB_PCI_IOPORTBASE + COLREGOFFSET
#define SISCOLDATA SISUSB_PCI_IOPORTBASE + COLREGOFFSET + 1
#define SISCOL2IDX SISPART5
#define SISCOL2DATA SISPART5 + 1
#define SISPEL SISUSB_PCI_IOPORTBASE + PELMASKOFFSET
#define SISVGAEN SISUSB_PCI_IOPORTBASE + VGAENABLE
#define SISDACA SISCOLIDX
#define SISDACD SISCOLDATA
/* ioctl related */
/* Structure argument for SISUSB_GET_INFO ioctl */
struct sisusb_info {
__u32 sisusb_id; /* for identifying sisusb */
#define SISUSB_ID 0x53495355 /* Identify myself with 'SISU' */
__u8 sisusb_version;
__u8 sisusb_revision;
__u8 sisusb_patchlevel;
__u8 sisusb_gfxinit; /* graphics core initialized? */
__u32 sisusb_vrambase;
__u32 sisusb_mmiobase;
__u32 sisusb_iobase;
__u32 sisusb_pcibase;
__u32 sisusb_vramsize; /* framebuffer size in bytes */
__u32 sisusb_minor;
__u32 sisusb_fbdevactive; /* != 0 if framebuffer device active */
__u32 sisusb_conactive; /* != 0 if console driver active */
__u8 sisusb_reserved[28]; /* for future use */
};
struct sisusb_command {
__u8 operation; /* see below */
__u8 data0; /* operation dependent */
__u8 data1; /* operation dependent */
__u8 data2; /* operation dependent */
__u32 data3; /* operation dependent */
__u32 data4; /* for future use */
};
#define SUCMD_GET 0x01 /* for all: data0 = index, data3 = port */
#define SUCMD_SET 0x02 /* data1 = value */
#define SUCMD_SETOR 0x03 /* data1 = or */
#define SUCMD_SETAND 0x04 /* data1 = and */
#define SUCMD_SETANDOR 0x05 /* data1 = and, data2 = or */
#define SUCMD_SETMASK 0x06 /* data1 = data, data2 = mask */
#define SUCMD_CLRSCR 0x07 /* data0:1:2 = length, data3 = address */
#define SUCMD_HANDLETEXTMODE 0x08 /* Reset/destroy text mode */
#define SUCMD_SETMODE 0x09 /* Set a display mode (data3 = SiS mode) */
#define SUCMD_SETVESAMODE 0x0a /* Set a display mode (data3 = VESA mode) */
#define SISUSB_COMMAND _IOWR(0xF3,0x3D,struct sisusb_command)
#define SISUSB_GET_CONFIG_SIZE _IOR(0xF3,0x3E,__u32)
#define SISUSB_GET_CONFIG _IOR(0xF3,0x3F,struct sisusb_info)
#endif /* SISUSB_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,960 @@
/*
* sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
*
* Display mode initializing code
*
* Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria
*
* If distributed as part of the Linux kernel, this code is licensed under the
* terms of the GPL v2.
*
* Otherwise, the following license terms apply:
*
* * Redistribution and use in source and binary forms, with or without
* * modification, are permitted provided that the following conditions
* * are met:
* * 1) Redistributions of source code must retain the above copyright
* * notice, this list of conditions and the following disclaimer.
* * 2) Redistributions in binary form must reproduce the above copyright
* * notice, this list of conditions and the following disclaimer in the
* * documentation and/or other materials provided with the distribution.
* * 3) The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission.
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Thomas Winischhofer <thomas@winischhofer.net>
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "sisusb.h"
#ifdef INCL_SISUSB_CON
#include "sisusb_init.h"
/*********************************************/
/* POINTER INITIALIZATION */
/*********************************************/
static void SiSUSB_InitPtr(struct SiS_Private *SiS_Pr)
{
SiS_Pr->SiS_ModeResInfo = SiSUSB_ModeResInfo;
SiS_Pr->SiS_StandTable = SiSUSB_StandTable;
SiS_Pr->SiS_SModeIDTable = SiSUSB_SModeIDTable;
SiS_Pr->SiS_EModeIDTable = SiSUSB_EModeIDTable;
SiS_Pr->SiS_RefIndex = SiSUSB_RefIndex;
SiS_Pr->SiS_CRT1Table = SiSUSB_CRT1Table;
SiS_Pr->SiS_VCLKData = SiSUSB_VCLKData;
}
/*********************************************/
/* HELPER: SetReg, GetReg */
/*********************************************/
static void
SiS_SetReg(struct SiS_Private *SiS_Pr, unsigned long port,
unsigned short index, unsigned short data)
{
sisusb_setidxreg(SiS_Pr->sisusb, port, index, data);
}
static void
SiS_SetRegByte(struct SiS_Private *SiS_Pr, unsigned long port,
unsigned short data)
{
sisusb_setreg(SiS_Pr->sisusb, port, data);
}
static unsigned char
SiS_GetReg(struct SiS_Private *SiS_Pr, unsigned long port, unsigned short index)
{
u8 data;
sisusb_getidxreg(SiS_Pr->sisusb, port, index, &data);
return data;
}
static unsigned char
SiS_GetRegByte(struct SiS_Private *SiS_Pr, unsigned long port)
{
u8 data;
sisusb_getreg(SiS_Pr->sisusb, port, &data);
return data;
}
static void
SiS_SetRegANDOR(struct SiS_Private *SiS_Pr, unsigned long port,
unsigned short index, unsigned short DataAND,
unsigned short DataOR)
{
sisusb_setidxregandor(SiS_Pr->sisusb, port, index, DataAND, DataOR);
}
static void
SiS_SetRegAND(struct SiS_Private *SiS_Pr, unsigned long port,
unsigned short index, unsigned short DataAND)
{
sisusb_setidxregand(SiS_Pr->sisusb, port, index, DataAND);
}
static void
SiS_SetRegOR(struct SiS_Private *SiS_Pr, unsigned long port,
unsigned short index, unsigned short DataOR)
{
sisusb_setidxregor(SiS_Pr->sisusb, port, index, DataOR);
}
/*********************************************/
/* HELPER: DisplayOn, DisplayOff */
/*********************************************/
static void SiS_DisplayOn(struct SiS_Private *SiS_Pr)
{
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, 0xDF);
}
/*********************************************/
/* HELPER: Init Port Addresses */
/*********************************************/
static void SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr)
{
SiS_Pr->SiS_P3c4 = BaseAddr + 0x14;
SiS_Pr->SiS_P3d4 = BaseAddr + 0x24;
SiS_Pr->SiS_P3c0 = BaseAddr + 0x10;
SiS_Pr->SiS_P3ce = BaseAddr + 0x1e;
SiS_Pr->SiS_P3c2 = BaseAddr + 0x12;
SiS_Pr->SiS_P3ca = BaseAddr + 0x1a;
SiS_Pr->SiS_P3c6 = BaseAddr + 0x16;
SiS_Pr->SiS_P3c7 = BaseAddr + 0x17;
SiS_Pr->SiS_P3c8 = BaseAddr + 0x18;
SiS_Pr->SiS_P3c9 = BaseAddr + 0x19;
SiS_Pr->SiS_P3cb = BaseAddr + 0x1b;
SiS_Pr->SiS_P3cc = BaseAddr + 0x1c;
SiS_Pr->SiS_P3cd = BaseAddr + 0x1d;
SiS_Pr->SiS_P3da = BaseAddr + 0x2a;
SiS_Pr->SiS_Part1Port = BaseAddr + SIS_CRT2_PORT_04;
}
/*********************************************/
/* HELPER: GetSysFlags */
/*********************************************/
static void SiS_GetSysFlags(struct SiS_Private *SiS_Pr)
{
SiS_Pr->SiS_MyCR63 = 0x63;
}
/*********************************************/
/* HELPER: Init PCI & Engines */
/*********************************************/
static void SiSInitPCIetc(struct SiS_Private *SiS_Pr)
{
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x20, 0xa1);
/* - Enable 2D (0x40)
* - Enable 3D (0x02)
* - Enable 3D vertex command fetch (0x10)
* - Enable 3D command parser (0x08)
* - Enable 3D G/L transformation engine (0x80)
*/
SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1E, 0xDA);
}
/*********************************************/
/* HELPER: SET SEGMENT REGISTERS */
/*********************************************/
static void SiS_SetSegRegLower(struct SiS_Private *SiS_Pr, unsigned short value)
{
unsigned short temp;
value &= 0x00ff;
temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb) & 0xf0;
temp |= (value >> 4);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb, temp);
temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd) & 0xf0;
temp |= (value & 0x0f);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd, temp);
}
static void SiS_SetSegRegUpper(struct SiS_Private *SiS_Pr, unsigned short value)
{
unsigned short temp;
value &= 0x00ff;
temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb) & 0x0f;
temp |= (value & 0xf0);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb, temp);
temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd) & 0x0f;
temp |= (value << 4);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd, temp);
}
static void SiS_SetSegmentReg(struct SiS_Private *SiS_Pr, unsigned short value)
{
SiS_SetSegRegLower(SiS_Pr, value);
SiS_SetSegRegUpper(SiS_Pr, value);
}
static void SiS_ResetSegmentReg(struct SiS_Private *SiS_Pr)
{
SiS_SetSegmentReg(SiS_Pr, 0);
}
static void
SiS_SetSegmentRegOver(struct SiS_Private *SiS_Pr, unsigned short value)
{
unsigned short temp = value >> 8;
temp &= 0x07;
temp |= (temp << 4);
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1d, temp);
SiS_SetSegmentReg(SiS_Pr, value);
}
static void SiS_ResetSegmentRegOver(struct SiS_Private *SiS_Pr)
{
SiS_SetSegmentRegOver(SiS_Pr, 0);
}
static void SiS_ResetSegmentRegisters(struct SiS_Private *SiS_Pr)
{
SiS_ResetSegmentReg(SiS_Pr);
SiS_ResetSegmentRegOver(SiS_Pr);
}
/*********************************************/
/* HELPER: SearchModeID */
/*********************************************/
static int
SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo,
unsigned short *ModeIdIndex)
{
if ((*ModeNo) <= 0x13) {
if ((*ModeNo) != 0x03)
return 0;
(*ModeIdIndex) = 0;
} else {
for (*ModeIdIndex = 0;; (*ModeIdIndex)++) {
if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID ==
(*ModeNo))
break;
if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID ==
0xFF)
return 0;
}
}
return 1;
}
/*********************************************/
/* HELPER: ENABLE CRT1 */
/*********************************************/
static void SiS_HandleCRT1(struct SiS_Private *SiS_Pr)
{
/* Enable CRT1 gating */
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, SiS_Pr->SiS_MyCR63, 0xbf);
}
/*********************************************/
/* HELPER: GetColorDepth */
/*********************************************/
static unsigned short
SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short ModeIdIndex)
{
static const unsigned short ColorDepth[6] = { 1, 2, 4, 4, 6, 8 };
unsigned short modeflag;
short index;
if (ModeNo <= 0x13) {
modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag;
} else {
modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
}
index = (modeflag & ModeTypeMask) - ModeEGA;
if (index < 0)
index = 0;
return ColorDepth[index];
}
/*********************************************/
/* HELPER: GetOffset */
/*********************************************/
static unsigned short
SiS_GetOffset(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short ModeIdIndex, unsigned short rrti)
{
unsigned short xres, temp, colordepth, infoflag;
infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag;
xres = SiS_Pr->SiS_RefIndex[rrti].XRes;
colordepth = SiS_GetColorDepth(SiS_Pr, ModeNo, ModeIdIndex);
temp = xres / 16;
if (infoflag & InterlaceMode)
temp <<= 1;
temp *= colordepth;
if (xres % 16)
temp += (colordepth >> 1);
return temp;
}
/*********************************************/
/* SEQ */
/*********************************************/
static void
SiS_SetSeqRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
{
unsigned char SRdata;
int i;
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x00, 0x03);
SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[0] | 0x20;
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, SRdata);
for (i = 2; i <= 4; i++) {
SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[i - 1];
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, i, SRdata);
}
}
/*********************************************/
/* MISC */
/*********************************************/
static void
SiS_SetMiscRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
{
unsigned char Miscdata = SiS_Pr->SiS_StandTable[StandTableIndex].MISC;
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c2, Miscdata);
}
/*********************************************/
/* CRTC */
/*********************************************/
static void
SiS_SetCRTCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
{
unsigned char CRTCdata;
unsigned short i;
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, 0x11, 0x7f);
for (i = 0; i <= 0x18; i++) {
CRTCdata = SiS_Pr->SiS_StandTable[StandTableIndex].CRTC[i];
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, i, CRTCdata);
}
}
/*********************************************/
/* ATT */
/*********************************************/
static void
SiS_SetATTRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
{
unsigned char ARdata;
unsigned short i;
for (i = 0; i <= 0x13; i++) {
ARdata = SiS_Pr->SiS_StandTable[StandTableIndex].ATTR[i];
SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, i);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, ARdata);
}
SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x14);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x00);
SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x20);
SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
}
/*********************************************/
/* GRC */
/*********************************************/
static void
SiS_SetGRCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
{
unsigned char GRdata;
unsigned short i;
for (i = 0; i <= 0x08; i++) {
GRdata = SiS_Pr->SiS_StandTable[StandTableIndex].GRC[i];
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3ce, i, GRdata);
}
if (SiS_Pr->SiS_ModeType > ModeVGA) {
/* 256 color disable */
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3ce, 0x05, 0xBF);
}
}
/*********************************************/
/* CLEAR EXTENDED REGISTERS */
/*********************************************/
static void SiS_ClearExt1Regs(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
{
int i;
for (i = 0x0A; i <= 0x0E; i++) {
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, i, 0x00);
}
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x37, 0xFE);
}
/*********************************************/
/* Get rate index */
/*********************************************/
static unsigned short
SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short ModeIdIndex)
{
unsigned short rrti, i, index, temp;
if (ModeNo <= 0x13)
return 0xFFFF;
index = SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x33) & 0x0F;
if (index > 0)
index--;
rrti = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].REFindex;
ModeNo = SiS_Pr->SiS_RefIndex[rrti].ModeID;
i = 0;
do {
if (SiS_Pr->SiS_RefIndex[rrti + i].ModeID != ModeNo)
break;
temp =
SiS_Pr->SiS_RefIndex[rrti + i].Ext_InfoFlag & ModeTypeMask;
if (temp < SiS_Pr->SiS_ModeType)
break;
i++;
index--;
} while (index != 0xFFFF);
i--;
return (rrti + i);
}
/*********************************************/
/* SYNC */
/*********************************************/
static void SiS_SetCRT1Sync(struct SiS_Private *SiS_Pr, unsigned short rrti)
{
unsigned short sync = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag >> 8;
sync &= 0xC0;
sync |= 0x2f;
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c2, sync);
}
/*********************************************/
/* CRTC/2 */
/*********************************************/
static void
SiS_SetCRT1CRTC(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short ModeIdIndex, unsigned short rrti)
{
unsigned char index;
unsigned short temp, i, j, modeflag;
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, 0x11, 0x7f);
modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRT1CRTC;
for (i = 0, j = 0; i <= 7; i++, j++) {
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
SiS_Pr->SiS_CRT1Table[index].CR[i]);
}
for (j = 0x10; i <= 10; i++, j++) {
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
SiS_Pr->SiS_CRT1Table[index].CR[i]);
}
for (j = 0x15; i <= 12; i++, j++) {
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
SiS_Pr->SiS_CRT1Table[index].CR[i]);
}
for (j = 0x0A; i <= 15; i++, j++) {
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, j,
SiS_Pr->SiS_CRT1Table[index].CR[i]);
}
temp = SiS_Pr->SiS_CRT1Table[index].CR[16] & 0xE0;
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0E, temp);
temp = ((SiS_Pr->SiS_CRT1Table[index].CR[16]) & 0x01) << 5;
if (modeflag & DoubleScanMode)
temp |= 0x80;
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3d4, 0x09, 0x5F, temp);
if (SiS_Pr->SiS_ModeType > ModeVGA)
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x14, 0x4F);
}
/*********************************************/
/* OFFSET & PITCH */
/*********************************************/
/* (partly overruled by SetPitch() in XF86) */
/*********************************************/
static void
SiS_SetCRT1Offset(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short ModeIdIndex, unsigned short rrti)
{
unsigned short du = SiS_GetOffset(SiS_Pr, ModeNo, ModeIdIndex, rrti);
unsigned short infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag;
unsigned short temp;
temp = (du >> 8) & 0x0f;
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0E, 0xF0, temp);
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x13, (du & 0xFF));
if (infoflag & InterlaceMode)
du >>= 1;
du <<= 5;
temp = (du >> 8) & 0xff;
if (du & 0xff)
temp++;
temp++;
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x10, temp);
}
/*********************************************/
/* VCLK */
/*********************************************/
static void
SiS_SetCRT1VCLK(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short rrti)
{
unsigned short index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRTVCLK;
unsigned short clka = SiS_Pr->SiS_VCLKData[index].SR2B;
unsigned short clkb = SiS_Pr->SiS_VCLKData[index].SR2C;
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x31, 0xCF);
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2B, clka);
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2C, clkb);
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2D, 0x01);
}
/*********************************************/
/* FIFO */
/*********************************************/
static void
SiS_SetCRT1FIFO_310(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short mi)
{
unsigned short modeflag = SiS_Pr->SiS_EModeIDTable[mi].Ext_ModeFlag;
/* disable auto-threshold */
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x3D, 0xFE);
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x08, 0xAE);
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x09, 0xF0);
if (ModeNo <= 0x13)
return;
if ((!(modeflag & DoubleScanMode)) || (!(modeflag & HalfDCLK))) {
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x08, 0x34);
SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x3D, 0x01);
}
}
/*********************************************/
/* MODE REGISTERS */
/*********************************************/
static void
SiS_SetVCLKState(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short rrti)
{
unsigned short data = 0, VCLK = 0, index = 0;
if (ModeNo > 0x13) {
index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRTVCLK;
VCLK = SiS_Pr->SiS_VCLKData[index].CLOCK;
}
if (VCLK >= 166)
data |= 0x0c;
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x32, 0xf3, data);
if (VCLK >= 166)
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1f, 0xe7);
/* DAC speed */
data = 0x03;
if (VCLK >= 260)
data = 0x00;
else if (VCLK >= 160)
data = 0x01;
else if (VCLK >= 135)
data = 0x02;
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x07, 0xF8, data);
}
static void
SiS_SetCRT1ModeRegs(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short ModeIdIndex, unsigned short rrti)
{
unsigned short data, infoflag = 0, modeflag;
if (ModeNo <= 0x13)
modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag;
else {
modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag;
}
/* Disable DPMS */
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1F, 0x3F);
data = 0;
if (ModeNo > 0x13) {
if (SiS_Pr->SiS_ModeType > ModeEGA) {
data |= 0x02;
data |= ((SiS_Pr->SiS_ModeType - ModeVGA) << 2);
}
if (infoflag & InterlaceMode)
data |= 0x20;
}
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x06, 0xC0, data);
data = 0;
if (infoflag & InterlaceMode) {
/* data = (Hsync / 8) - ((Htotal / 8) / 2) + 3 */
unsigned short hrs =
(SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x04) |
((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0xc0) << 2))
- 3;
unsigned short hto =
(SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x00) |
((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0x03) << 8))
+ 5;
data = hrs - (hto >> 1) + 3;
}
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x19, (data & 0xFF));
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3d4, 0x1a, 0xFC, (data >> 8));
if (modeflag & HalfDCLK)
SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, 0x08);
data = 0;
if (modeflag & LineCompareOff)
data = 0x08;
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0F, 0xB7, data);
if ((SiS_Pr->SiS_ModeType == ModeEGA) && (ModeNo > 0x13))
SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0F, 0x40);
SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x31, 0xfb);
data = 0x60;
if (SiS_Pr->SiS_ModeType != ModeText) {
data ^= 0x60;
if (SiS_Pr->SiS_ModeType != ModeEGA)
data ^= 0xA0;
}
SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x21, 0x1F, data);
SiS_SetVCLKState(SiS_Pr, ModeNo, rrti);
if (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x31) & 0x40)
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x52, 0x2c);
else
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x52, 0x6c);
}
/*********************************************/
/* LOAD DAC */
/*********************************************/
static void
SiS_WriteDAC(struct SiS_Private *SiS_Pr, unsigned long DACData,
unsigned short shiftflag, unsigned short dl, unsigned short ah,
unsigned short al, unsigned short dh)
{
unsigned short d1, d2, d3;
switch (dl) {
case 0:
d1 = dh;
d2 = ah;
d3 = al;
break;
case 1:
d1 = ah;
d2 = al;
d3 = dh;
break;
default:
d1 = al;
d2 = dh;
d3 = ah;
}
SiS_SetRegByte(SiS_Pr, DACData, (d1 << shiftflag));
SiS_SetRegByte(SiS_Pr, DACData, (d2 << shiftflag));
SiS_SetRegByte(SiS_Pr, DACData, (d3 << shiftflag));
}
static void
SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short mi)
{
unsigned short data, data2, time, i, j, k, m, n, o;
unsigned short si, di, bx, sf;
unsigned long DACAddr, DACData;
const unsigned char *table = NULL;
if (ModeNo < 0x13)
data = SiS_Pr->SiS_SModeIDTable[mi].St_ModeFlag;
else
data = SiS_Pr->SiS_EModeIDTable[mi].Ext_ModeFlag;
data &= DACInfoFlag;
j = time = 64;
if (data == 0x00)
table = SiS_MDA_DAC;
else if (data == 0x08)
table = SiS_CGA_DAC;
else if (data == 0x10)
table = SiS_EGA_DAC;
else {
j = 16;
time = 256;
table = SiS_VGA_DAC;
}
DACAddr = SiS_Pr->SiS_P3c8;
DACData = SiS_Pr->SiS_P3c9;
sf = 0;
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c6, 0xFF);
SiS_SetRegByte(SiS_Pr, DACAddr, 0x00);
for (i = 0; i < j; i++) {
data = table[i];
for (k = 0; k < 3; k++) {
data2 = 0;
if (data & 0x01)
data2 += 0x2A;
if (data & 0x02)
data2 += 0x15;
SiS_SetRegByte(SiS_Pr, DACData, (data2 << sf));
data >>= 2;
}
}
if (time == 256) {
for (i = 16; i < 32; i++) {
data = table[i] << sf;
for (k = 0; k < 3; k++)
SiS_SetRegByte(SiS_Pr, DACData, data);
}
si = 32;
for (m = 0; m < 9; m++) {
di = si;
bx = si + 4;
for (n = 0; n < 3; n++) {
for (o = 0; o < 5; o++) {
SiS_WriteDAC(SiS_Pr, DACData, sf, n,
table[di], table[bx],
table[si]);
si++;
}
si -= 2;
for (o = 0; o < 3; o++) {
SiS_WriteDAC(SiS_Pr, DACData, sf, n,
table[di], table[si],
table[bx]);
si--;
}
}
si += 5;
}
}
}
/*********************************************/
/* SET CRT1 REGISTER GROUP */
/*********************************************/
static void
SiS_SetCRT1Group(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
unsigned short ModeIdIndex)
{
unsigned short StandTableIndex, rrti;
SiS_Pr->SiS_CRT1Mode = ModeNo;
if (ModeNo <= 0x13)
StandTableIndex = 0;
else
StandTableIndex = 1;
SiS_ResetSegmentRegisters(SiS_Pr);
SiS_SetSeqRegs(SiS_Pr, StandTableIndex);
SiS_SetMiscRegs(SiS_Pr, StandTableIndex);
SiS_SetCRTCRegs(SiS_Pr, StandTableIndex);
SiS_SetATTRegs(SiS_Pr, StandTableIndex);
SiS_SetGRCRegs(SiS_Pr, StandTableIndex);
SiS_ClearExt1Regs(SiS_Pr, ModeNo);
rrti = SiS_GetRatePtr(SiS_Pr, ModeNo, ModeIdIndex);
if (rrti != 0xFFFF) {
SiS_SetCRT1Sync(SiS_Pr, rrti);
SiS_SetCRT1CRTC(SiS_Pr, ModeNo, ModeIdIndex, rrti);
SiS_SetCRT1Offset(SiS_Pr, ModeNo, ModeIdIndex, rrti);
SiS_SetCRT1VCLK(SiS_Pr, ModeNo, rrti);
}
SiS_SetCRT1FIFO_310(SiS_Pr, ModeNo, ModeIdIndex);
SiS_SetCRT1ModeRegs(SiS_Pr, ModeNo, ModeIdIndex, rrti);
SiS_LoadDAC(SiS_Pr, ModeNo, ModeIdIndex);
SiS_DisplayOn(SiS_Pr);
}
/*********************************************/
/* SiSSetMode() */
/*********************************************/
int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
{
unsigned short ModeIdIndex;
unsigned long BaseAddr = SiS_Pr->IOAddress;
SiSUSB_InitPtr(SiS_Pr);
SiSUSBRegInit(SiS_Pr, BaseAddr);
SiS_GetSysFlags(SiS_Pr);
if (!(SiS_SearchModeID(SiS_Pr, &ModeNo, &ModeIdIndex)))
return 0;
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x05, 0x86);
SiSInitPCIetc(SiS_Pr);
ModeNo &= 0x7f;
SiS_Pr->SiS_ModeType =
SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag & ModeTypeMask;
SiS_Pr->SiS_SetFlag = LowModeTests;
/* Set mode on CRT1 */
SiS_SetCRT1Group(SiS_Pr, ModeNo, ModeIdIndex);
SiS_HandleCRT1(SiS_Pr);
SiS_DisplayOn(SiS_Pr);
SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c6, 0xFF);
/* Store mode number */
SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x34, ModeNo);
return 1;
}
int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo)
{
unsigned short ModeNo = 0;
int i;
SiSUSB_InitPtr(SiS_Pr);
if (VModeNo == 0x03) {
ModeNo = 0x03;
} else {
i = 0;
do {
if (SiS_Pr->SiS_EModeIDTable[i].Ext_VESAID == VModeNo) {
ModeNo = SiS_Pr->SiS_EModeIDTable[i].Ext_ModeID;
break;
}
} while (SiS_Pr->SiS_EModeIDTable[i++].Ext_ModeID != 0xff);
}
if (!ModeNo)
return 0;
return SiSUSBSetMode(SiS_Pr, ModeNo);
}
#endif /* INCL_SISUSB_CON */

View File

@@ -0,0 +1,841 @@
/* $XFree86$ */
/* $XdotOrg$ */
/*
* Data and prototypes for init.c
*
* Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria
*
* If distributed as part of the Linux kernel, the following license terms
* apply:
*
* * 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 named License,
* * or 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*
* Otherwise, the following license terms apply:
*
* * Redistribution and use in source and binary forms, with or without
* * modification, are permitted provided that the following conditions
* * are met:
* * 1) Redistributions of source code must retain the above copyright
* * notice, this list of conditions and the following disclaimer.
* * 2) Redistributions in binary form must reproduce the above copyright
* * notice, this list of conditions and the following disclaimer in the
* * documentation and/or other materials provided with the distribution.
* * 3) The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission.
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Thomas Winischhofer <thomas@winischhofer.net>
*
*/
#ifndef _SISUSB_INIT_H_
#define _SISUSB_INIT_H_
/* SiS_ModeType */
#define ModeText 0x00
#define ModeCGA 0x01
#define ModeEGA 0x02
#define ModeVGA 0x03
#define Mode15Bpp 0x04
#define Mode16Bpp 0x05
#define Mode24Bpp 0x06
#define Mode32Bpp 0x07
#define ModeTypeMask 0x07
#define IsTextMode 0x07
#define DACInfoFlag 0x0018
#define MemoryInfoFlag 0x01E0
#define MemorySizeShift 5
/* modeflag */
#define Charx8Dot 0x0200
#define LineCompareOff 0x0400
#define CRT2Mode 0x0800
#define HalfDCLK 0x1000
#define NoSupportSimuTV 0x2000
#define NoSupportLCDScale 0x4000 /* SiS bridge: No scaling possible (no matter what panel) */
#define DoubleScanMode 0x8000
/* Infoflag */
#define SupportTV 0x0008
#define SupportTV1024 0x0800
#define SupportCHTV 0x0800
#define Support64048060Hz 0x0800 /* Special for 640x480 LCD */
#define SupportHiVision 0x0010
#define SupportYPbPr750p 0x1000
#define SupportLCD 0x0020
#define SupportRAMDAC2 0x0040 /* All (<= 100Mhz) */
#define SupportRAMDAC2_135 0x0100 /* All except DH (<= 135Mhz) */
#define SupportRAMDAC2_162 0x0200 /* B, C (<= 162Mhz) */
#define SupportRAMDAC2_202 0x0400 /* C (<= 202Mhz) */
#define InterlaceMode 0x0080
#define SyncPP 0x0000
#define SyncPN 0x4000
#define SyncNP 0x8000
#define SyncNN 0xc000
/* SetFlag */
#define ProgrammingCRT2 0x0001
#define LowModeTests 0x0002
#define LCDVESATiming 0x0008
#define EnableLVDSDDA 0x0010
#define SetDispDevSwitchFlag 0x0020
#define CheckWinDos 0x0040
#define SetDOSMode 0x0080
/* Index in ModeResInfo table */
#define SIS_RI_320x200 0
#define SIS_RI_320x240 1
#define SIS_RI_320x400 2
#define SIS_RI_400x300 3
#define SIS_RI_512x384 4
#define SIS_RI_640x400 5
#define SIS_RI_640x480 6
#define SIS_RI_800x600 7
#define SIS_RI_1024x768 8
#define SIS_RI_1280x1024 9
#define SIS_RI_1600x1200 10
#define SIS_RI_1920x1440 11
#define SIS_RI_2048x1536 12
#define SIS_RI_720x480 13
#define SIS_RI_720x576 14
#define SIS_RI_1280x960 15
#define SIS_RI_800x480 16
#define SIS_RI_1024x576 17
#define SIS_RI_1280x720 18
#define SIS_RI_856x480 19
#define SIS_RI_1280x768 20
#define SIS_RI_1400x1050 21
#define SIS_RI_1152x864 22 /* Up to here SiS conforming */
#define SIS_RI_848x480 23
#define SIS_RI_1360x768 24
#define SIS_RI_1024x600 25
#define SIS_RI_1152x768 26
#define SIS_RI_768x576 27
#define SIS_RI_1360x1024 28
#define SIS_RI_1680x1050 29
#define SIS_RI_1280x800 30
#define SIS_RI_1920x1080 31
#define SIS_RI_960x540 32
#define SIS_RI_960x600 33
#define SIS_VIDEO_CAPTURE 0x00 - 0x30
#define SIS_VIDEO_PLAYBACK 0x02 - 0x30
#define SIS_CRT2_PORT_04 0x04 - 0x30
/* Mode numbers */
static const unsigned short ModeIndex_320x200[] = { 0x59, 0x41, 0x00, 0x4f };
static const unsigned short ModeIndex_320x240[] = { 0x50, 0x56, 0x00, 0x53 };
static const unsigned short ModeIndex_400x300[] = { 0x51, 0x57, 0x00, 0x54 };
static const unsigned short ModeIndex_512x384[] = { 0x52, 0x58, 0x00, 0x5c };
static const unsigned short ModeIndex_640x400[] = { 0x2f, 0x5d, 0x00, 0x5e };
static const unsigned short ModeIndex_640x480[] = { 0x2e, 0x44, 0x00, 0x62 };
static const unsigned short ModeIndex_720x480[] = { 0x31, 0x33, 0x00, 0x35 };
static const unsigned short ModeIndex_720x576[] = { 0x32, 0x34, 0x00, 0x36 };
static const unsigned short ModeIndex_768x576[] = { 0x5f, 0x60, 0x00, 0x61 };
static const unsigned short ModeIndex_800x480[] = { 0x70, 0x7a, 0x00, 0x76 };
static const unsigned short ModeIndex_800x600[] = { 0x30, 0x47, 0x00, 0x63 };
static const unsigned short ModeIndex_848x480[] = { 0x39, 0x3b, 0x00, 0x3e };
static const unsigned short ModeIndex_856x480[] = { 0x3f, 0x42, 0x00, 0x45 };
static const unsigned short ModeIndex_960x540[] = { 0x1d, 0x1e, 0x00, 0x1f };
static const unsigned short ModeIndex_960x600[] = { 0x20, 0x21, 0x00, 0x22 };
static const unsigned short ModeIndex_1024x768[] = { 0x38, 0x4a, 0x00, 0x64 };
static const unsigned short ModeIndex_1024x576[] = { 0x71, 0x74, 0x00, 0x77 };
static const unsigned short ModeIndex_1152x864[] = { 0x29, 0x2a, 0x00, 0x2b };
static const unsigned short ModeIndex_1280x720[] = { 0x79, 0x75, 0x00, 0x78 };
static const unsigned short ModeIndex_1280x768[] = { 0x23, 0x24, 0x00, 0x25 };
static const unsigned short ModeIndex_1280x1024[] = { 0x3a, 0x4d, 0x00, 0x65 };
static const unsigned char SiS_MDA_DAC[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F
};
static const unsigned char SiS_CGA_DAC[] = {
0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F
};
static const unsigned char SiS_EGA_DAC[] = {
0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x05, 0x15,
0x20, 0x30, 0x24, 0x34, 0x21, 0x31, 0x25, 0x35,
0x08, 0x18, 0x0C, 0x1C, 0x09, 0x19, 0x0D, 0x1D,
0x28, 0x38, 0x2C, 0x3C, 0x29, 0x39, 0x2D, 0x3D,
0x02, 0x12, 0x06, 0x16, 0x03, 0x13, 0x07, 0x17,
0x22, 0x32, 0x26, 0x36, 0x23, 0x33, 0x27, 0x37,
0x0A, 0x1A, 0x0E, 0x1E, 0x0B, 0x1B, 0x0F, 0x1F,
0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F
};
static const unsigned char SiS_VGA_DAC[] = {
0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
0x00, 0x05, 0x08, 0x0B, 0x0E, 0x11, 0x14, 0x18,
0x1C, 0x20, 0x24, 0x28, 0x2D, 0x32, 0x38, 0x3F,
0x00, 0x10, 0x1F, 0x2F, 0x3F, 0x1F, 0x27, 0x2F,
0x37, 0x3F, 0x2D, 0x31, 0x36, 0x3A, 0x3F, 0x00,
0x07, 0x0E, 0x15, 0x1C, 0x0E, 0x11, 0x15, 0x18,
0x1C, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x00, 0x04,
0x08, 0x0C, 0x10, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
0x0B, 0x0C, 0x0D, 0x0F, 0x10
};
static const struct SiS_St SiSUSB_SModeIDTable[] = {
{0x03, 0x0010, 0x18, 0x02, 0x02, 0x00, 0x01, 0x03, 0x40},
{0xff, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
static const struct SiS_StResInfo_S SiSUSB_StResInfo[] = {
{640, 400},
{640, 350},
{720, 400},
{720, 350},
{640, 480}
};
static const struct SiS_ModeResInfo SiSUSB_ModeResInfo[] = {
{320, 200, 8, 8}, /* 0x00 */
{320, 240, 8, 8}, /* 0x01 */
{320, 400, 8, 8}, /* 0x02 */
{400, 300, 8, 8}, /* 0x03 */
{512, 384, 8, 8}, /* 0x04 */
{640, 400, 8, 16}, /* 0x05 */
{640, 480, 8, 16}, /* 0x06 */
{800, 600, 8, 16}, /* 0x07 */
{1024, 768, 8, 16}, /* 0x08 */
{1280, 1024, 8, 16}, /* 0x09 */
{1600, 1200, 8, 16}, /* 0x0a */
{1920, 1440, 8, 16}, /* 0x0b */
{2048, 1536, 8, 16}, /* 0x0c */
{720, 480, 8, 16}, /* 0x0d */
{720, 576, 8, 16}, /* 0x0e */
{1280, 960, 8, 16}, /* 0x0f */
{800, 480, 8, 16}, /* 0x10 */
{1024, 576, 8, 16}, /* 0x11 */
{1280, 720, 8, 16}, /* 0x12 */
{856, 480, 8, 16}, /* 0x13 */
{1280, 768, 8, 16}, /* 0x14 */
{1400, 1050, 8, 16}, /* 0x15 */
{1152, 864, 8, 16}, /* 0x16 */
{848, 480, 8, 16}, /* 0x17 */
{1360, 768, 8, 16}, /* 0x18 */
{1024, 600, 8, 16}, /* 0x19 */
{1152, 768, 8, 16}, /* 0x1a */
{768, 576, 8, 16}, /* 0x1b */
{1360, 1024, 8, 16}, /* 0x1c */
{1680, 1050, 8, 16}, /* 0x1d */
{1280, 800, 8, 16}, /* 0x1e */
{1920, 1080, 8, 16}, /* 0x1f */
{960, 540, 8, 16}, /* 0x20 */
{960, 600, 8, 16} /* 0x21 */
};
static const struct SiS_StandTable SiSUSB_StandTable[] = {
/* MD_3_400 - mode 0x03 - 400 */
{
0x50, 0x18, 0x10, 0x1000,
{0x00, 0x03, 0x00, 0x02},
0x67,
{0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3,
0xff},
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x0c, 0x00, 0x0f, 0x08},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x00, 0xff}
},
/* Generic for VGA and higher */
{
0x00, 0x00, 0x00, 0x0000,
{0x01, 0x0f, 0x00, 0x0e},
0x23,
{0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xea, 0x8c, 0xdf, 0x28, 0x40, 0xe7, 0x04, 0xa3,
0xff},
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x01, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff}
}
};
static const struct SiS_Ext SiSUSB_EModeIDTable[] = {
{0x2e, 0x0a1b, 0x0101, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x8 */
{0x2f, 0x0a1b, 0x0100, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x05, 0x10, 0}, /* 640x400x8 */
{0x30, 0x2a1b, 0x0103, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x8 */
{0x31, 0x4a1b, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x8 */
{0x32, 0x4a1b, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x8 */
{0x33, 0x4a1d, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x16 */
{0x34, 0x6a1d, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x16 */
{0x35, 0x4a1f, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x32 */
{0x36, 0x6a1f, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x32 */
{0x38, 0x0a1b, 0x0105, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x8 */
{0x3a, 0x0e3b, 0x0107, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x8 */
{0x41, 0x9a1d, 0x010e, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x16 */
{0x44, 0x0a1d, 0x0111, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x16 */
{0x47, 0x2a1d, 0x0114, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x16 */
{0x4a, 0x0a3d, 0x0117, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x16 */
{0x4d, 0x0e7d, 0x011a, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x16 */
{0x50, 0x9a1b, 0x0132, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x8 */
{0x51, 0xba1b, 0x0133, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x8 */
{0x52, 0xba1b, 0x0134, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x8 */
{0x56, 0x9a1d, 0x0135, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x16 */
{0x57, 0xba1d, 0x0136, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x16 */
{0x58, 0xba1d, 0x0137, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x16 */
{0x59, 0x9a1b, 0x0138, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x8 */
{0x5c, 0xba1f, 0x0000, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x32 */
{0x5d, 0x0a1d, 0x0139, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x07, 0x10, 0}, /* 640x400x16 */
{0x5e, 0x0a1f, 0x0000, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x07, 0x10, 0}, /* 640x400x32 */
{0x62, 0x0a3f, 0x013a, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x32 */
{0x63, 0x2a3f, 0x013b, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x32 */
{0x64, 0x0a7f, 0x013c, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x32 */
{0x65, 0x0eff, 0x013d, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x32 */
{0x70, 0x6a1b, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x8 */
{0x71, 0x4a1b, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x8 */
{0x74, 0x4a1d, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x16 */
{0x75, 0x0a3d, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x16 */
{0x76, 0x6a1f, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x32 */
{0x77, 0x4a1f, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x32 */
{0x78, 0x0a3f, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x32 */
{0x79, 0x0a3b, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x8 */
{0x7a, 0x6a1d, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x16 */
{0x23, 0x0e3b, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x8 */
{0x24, 0x0e7d, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x16 */
{0x25, 0x0eff, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x32 */
{0x39, 0x6a1b, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28, -1}, /* 848x480 */
{0x3b, 0x6a3d, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28,
-1},
{0x3e, 0x6a7f, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28,
-1},
{0x3f, 0x6a1b, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a, -1}, /* 856x480 */
{0x42, 0x6a3d, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a,
-1},
{0x45, 0x6a7f, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a,
-1},
{0x4f, 0x9a1f, 0x0000, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x32 */
{0x53, 0x9a1f, 0x0000, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x32 */
{0x54, 0xba1f, 0x0000, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x32 */
{0x5f, 0x6a1b, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c, -1}, /* 768x576 */
{0x60, 0x6a1d, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c,
-1},
{0x61, 0x6a3f, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c,
-1},
{0x1d, 0x6a1b, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d, -1}, /* 960x540 */
{0x1e, 0x6a3d, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d,
-1},
{0x1f, 0x6a7f, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d,
-1},
{0x20, 0x6a1b, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e, -1}, /* 960x600 */
{0x21, 0x6a3d, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e,
-1},
{0x22, 0x6a7f, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e,
-1},
{0x29, 0x4e1b, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33, -1}, /* 1152x864 */
{0x2a, 0x4e3d, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33,
-1},
{0x2b, 0x4e7f, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33,
-1},
{0xff, 0x0000, 0x0000, 0, 0x00, 0x00, 0x00, 0x00, 0x00, -1}
};
static const struct SiS_Ext2 SiSUSB_RefIndex[] = {
{0x085f, 0x0d, 0x03, 0x05, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x0 */
{0x0067, 0x0e, 0x04, 0x05, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x1 */
{0x0067, 0x0f, 0x08, 0x48, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x2 */
{0x0067, 0x10, 0x07, 0x8b, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x3 */
{0x0047, 0x11, 0x0a, 0x00, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x4 */
{0x0047, 0x12, 0x0d, 0x00, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x5 */
{0x0047, 0x13, 0x13, 0x00, 0x05, 0x30, 800, 600, 0x20, 0x00, 0x00}, /* 0x6 */
{0x0107, 0x14, 0x1c, 0x00, 0x05, 0x30, 800, 600, 0x20, 0x00, 0x00}, /* 0x7 */
{0xc85f, 0x05, 0x00, 0x04, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0x8 */
{0xc067, 0x06, 0x02, 0x04, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0x9 */
{0xc067, 0x07, 0x02, 0x47, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xa */
{0xc067, 0x08, 0x03, 0x8a, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xb */
{0xc047, 0x09, 0x05, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xc */
{0xc047, 0x0a, 0x09, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xd */
{0xc047, 0x0b, 0x0e, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xe */
{0xc047, 0x0c, 0x15, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xf */
{0x487f, 0x04, 0x00, 0x00, 0x00, 0x2f, 640, 400, 0x30, 0x55, 0x6e}, /* 0x10 */
{0xc06f, 0x3c, 0x01, 0x06, 0x13, 0x31, 720, 480, 0x30, 0x00, 0x00}, /* 0x11 */
{0x006f, 0x3d, 0x6f, 0x06, 0x14, 0x32, 720, 576, 0x30, 0x00, 0x00}, /* 0x12 (6f was 03) */
{0x0087, 0x15, 0x06, 0x00, 0x06, 0x38, 1024, 768, 0x30, 0x00, 0x00}, /* 0x13 */
{0xc877, 0x16, 0x0b, 0x06, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x14 */
{0xc067, 0x17, 0x0f, 0x49, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x15 */
{0x0067, 0x18, 0x11, 0x00, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x16 */
{0x0047, 0x19, 0x16, 0x8c, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x17 */
{0x0107, 0x1a, 0x1b, 0x00, 0x06, 0x38, 1024, 768, 0x10, 0x00, 0x00}, /* 0x18 */
{0x0107, 0x1b, 0x1f, 0x00, 0x06, 0x38, 1024, 768, 0x10, 0x00, 0x00}, /* 0x19 */
{0x407f, 0x00, 0x00, 0x00, 0x00, 0x41, 320, 200, 0x30, 0x56, 0x4e}, /* 0x1a */
{0xc07f, 0x01, 0x00, 0x04, 0x04, 0x50, 320, 240, 0x30, 0x00, 0x00}, /* 0x1b */
{0x007f, 0x02, 0x04, 0x05, 0x05, 0x51, 400, 300, 0x30, 0x00, 0x00}, /* 0x1c */
{0xc077, 0x03, 0x0b, 0x06, 0x06, 0x52, 512, 384, 0x30, 0x00, 0x00}, /* 0x1d */
{0x0077, 0x32, 0x40, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x1e */
{0x0047, 0x33, 0x07, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x1f */
{0x0047, 0x34, 0x0a, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x20 */
{0x0077, 0x35, 0x0b, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x21 */
{0x0047, 0x36, 0x11, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x22 */
{0x0047, 0x37, 0x16, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x23 */
{0x1137, 0x38, 0x19, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x24 */
{0x1107, 0x39, 0x1e, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x25 */
{0x1307, 0x3a, 0x20, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x26 */
{0x0077, 0x42, 0x5b, 0x08, 0x11, 0x23, 1280, 768, 0x30, 0x00, 0x00}, /* 0x27 */
{0x0087, 0x45, 0x57, 0x00, 0x16, 0x39, 848, 480, 0x30, 0x00, 0x00}, /* 0x28 38Hzi */
{0xc067, 0x46, 0x55, 0x0b, 0x16, 0x39, 848, 480, 0x30, 0x00, 0x00}, /* 0x29 848x480-60Hz */
{0x0087, 0x47, 0x57, 0x00, 0x17, 0x3f, 856, 480, 0x30, 0x00, 0x00}, /* 0x2a 856x480-38Hzi */
{0xc067, 0x48, 0x57, 0x00, 0x17, 0x3f, 856, 480, 0x30, 0x00, 0x00}, /* 0x2b 856x480-60Hz */
{0x006f, 0x4d, 0x71, 0x06, 0x15, 0x5f, 768, 576, 0x30, 0x00, 0x00}, /* 0x2c 768x576-56Hz */
{0x0067, 0x52, 0x6a, 0x00, 0x1c, 0x1d, 960, 540, 0x30, 0x00, 0x00}, /* 0x2d 960x540 60Hz */
{0x0077, 0x53, 0x6b, 0x0b, 0x1d, 0x20, 960, 600, 0x30, 0x00, 0x00}, /* 0x2e 960x600 60Hz */
{0x0087, 0x1c, 0x11, 0x00, 0x07, 0x3a, 1280, 1024, 0x30, 0x00, 0x00}, /* 0x2f */
{0x0137, 0x1d, 0x19, 0x07, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x30 */
{0x0107, 0x1e, 0x1e, 0x00, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x31 */
{0x0207, 0x1f, 0x20, 0x00, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x32 */
{0x0127, 0x54, 0x6d, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x33 1152x864-60Hz */
{0x0127, 0x44, 0x19, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x34 1152x864-75Hz */
{0x0127, 0x4a, 0x1e, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x35 1152x864-85Hz */
{0xffff, 0x00, 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0x00, 0x00}
};
static const struct SiS_CRT1Table SiSUSB_CRT1Table[] = {
{{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0xbf, 0x1f,
0x9c, 0x8e, 0x8f, 0x96, 0xb9, 0x30, 0x00, 0x00,
0x00}}, /* 0x0 */
{{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0x0b, 0x3e,
0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x00,
0x00}}, /* 0x1 */
{{0x3d, 0x31, 0x31, 0x81, 0x37, 0x1f, 0x72, 0xf0,
0x58, 0x8c, 0x57, 0x57, 0x73, 0x20, 0x00, 0x05,
0x01}}, /* 0x2 */
{{0x4f, 0x3f, 0x3f, 0x93, 0x45, 0x0d, 0x24, 0xf5,
0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x01,
0x01}}, /* 0x3 */
{{0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
0x9c, 0x8e, 0x8f, 0x96, 0xb9, 0x30, 0x00, 0x05,
0x00}}, /* 0x4 */
{{0x5f, 0x4f, 0x4f, 0x83, 0x55, 0x81, 0x0b, 0x3e,
0xe9, 0x8b, 0xdf, 0xe8, 0x0c, 0x00, 0x00, 0x05,
0x00}}, /* 0x5 */
{{0x63, 0x4f, 0x4f, 0x87, 0x56, 0x9b, 0x06, 0x3e,
0xe8, 0x8a, 0xdf, 0xe7, 0x07, 0x00, 0x00, 0x01,
0x00}}, /* 0x6 */
{{0x64, 0x4f, 0x4f, 0x88, 0x55, 0x9d, 0xf2, 0x1f,
0xe0, 0x83, 0xdf, 0xdf, 0xf3, 0x10, 0x00, 0x01,
0x00}}, /* 0x7 */
{{0x63, 0x4f, 0x4f, 0x87, 0x5a, 0x81, 0xfb, 0x1f,
0xe0, 0x83, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x05,
0x00}}, /* 0x8 */
{{0x65, 0x4f, 0x4f, 0x89, 0x58, 0x80, 0xfb, 0x1f,
0xe0, 0x83, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x05,
0x61}}, /* 0x9 */
{{0x65, 0x4f, 0x4f, 0x89, 0x58, 0x80, 0x01, 0x3e,
0xe0, 0x83, 0xdf, 0xdf, 0x02, 0x00, 0x00, 0x05,
0x61}}, /* 0xa */
{{0x67, 0x4f, 0x4f, 0x8b, 0x58, 0x81, 0x0d, 0x3e,
0xe0, 0x83, 0xdf, 0xdf, 0x0e, 0x00, 0x00, 0x05,
0x61}}, /* 0xb */
{{0x65, 0x4f, 0x4f, 0x89, 0x57, 0x9f, 0xfb, 0x1f,
0xe6, 0x8a, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x01,
0x00}}, /* 0xc */
{{0x7b, 0x63, 0x63, 0x9f, 0x6a, 0x93, 0x6f, 0xf0,
0x58, 0x8a, 0x57, 0x57, 0x70, 0x20, 0x00, 0x05,
0x01}}, /* 0xd */
{{0x7f, 0x63, 0x63, 0x83, 0x6c, 0x1c, 0x72, 0xf0,
0x58, 0x8c, 0x57, 0x57, 0x73, 0x20, 0x00, 0x06,
0x01}}, /* 0xe */
{{0x7d, 0x63, 0x63, 0x81, 0x6e, 0x1d, 0x98, 0xf0,
0x7c, 0x82, 0x57, 0x57, 0x99, 0x00, 0x00, 0x06,
0x01}}, /* 0xf */
{{0x7f, 0x63, 0x63, 0x83, 0x69, 0x13, 0x6f, 0xf0,
0x58, 0x8b, 0x57, 0x57, 0x70, 0x20, 0x00, 0x06,
0x01}}, /* 0x10 */
{{0x7e, 0x63, 0x63, 0x82, 0x6b, 0x13, 0x75, 0xf0,
0x58, 0x8b, 0x57, 0x57, 0x76, 0x20, 0x00, 0x06,
0x01}}, /* 0x11 */
{{0x81, 0x63, 0x63, 0x85, 0x6d, 0x18, 0x7a, 0xf0,
0x58, 0x8b, 0x57, 0x57, 0x7b, 0x20, 0x00, 0x06,
0x61}}, /* 0x12 */
{{0x83, 0x63, 0x63, 0x87, 0x6e, 0x19, 0x81, 0xf0,
0x58, 0x8b, 0x57, 0x57, 0x82, 0x20, 0x00, 0x06,
0x61}}, /* 0x13 */
{{0x85, 0x63, 0x63, 0x89, 0x6f, 0x1a, 0x91, 0xf0,
0x58, 0x8b, 0x57, 0x57, 0x92, 0x20, 0x00, 0x06,
0x61}}, /* 0x14 */
{{0x99, 0x7f, 0x7f, 0x9d, 0x84, 0x1a, 0x96, 0x1f,
0x7f, 0x83, 0x7f, 0x7f, 0x97, 0x10, 0x00, 0x02,
0x00}}, /* 0x15 */
{{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf5,
0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
0x01}}, /* 0x16 */
{{0xa1, 0x7f, 0x7f, 0x85, 0x86, 0x97, 0x24, 0xf5,
0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
0x01}}, /* 0x17 */
{{0x9f, 0x7f, 0x7f, 0x83, 0x85, 0x91, 0x1e, 0xf5,
0x00, 0x83, 0xff, 0xff, 0x1f, 0x10, 0x00, 0x02,
0x01}}, /* 0x18 */
{{0xa7, 0x7f, 0x7f, 0x8b, 0x89, 0x95, 0x26, 0xf5,
0x00, 0x83, 0xff, 0xff, 0x27, 0x10, 0x00, 0x02,
0x01}}, /* 0x19 */
{{0xa9, 0x7f, 0x7f, 0x8d, 0x8c, 0x9a, 0x2c, 0xf5,
0x00, 0x83, 0xff, 0xff, 0x2d, 0x14, 0x00, 0x02,
0x62}}, /* 0x1a */
{{0xab, 0x7f, 0x7f, 0x8f, 0x8d, 0x9b, 0x35, 0xf5,
0x00, 0x83, 0xff, 0xff, 0x36, 0x14, 0x00, 0x02,
0x62}}, /* 0x1b */
{{0xcf, 0x9f, 0x9f, 0x93, 0xb2, 0x01, 0x14, 0xba,
0x00, 0x83, 0xff, 0xff, 0x15, 0x00, 0x00, 0x03,
0x00}}, /* 0x1c */
{{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x28, 0x5a,
0x00, 0x83, 0xff, 0xff, 0x29, 0x09, 0x00, 0x07,
0x01}}, /* 0x1d */
{{0xce, 0x9f, 0x9f, 0x92, 0xa5, 0x17, 0x28, 0x5a,
0x00, 0x83, 0xff, 0xff, 0x29, 0x09, 0x00, 0x07,
0x01}}, /* 0x1e */
{{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0x2e, 0x5a,
0x00, 0x83, 0xff, 0xff, 0x2f, 0x09, 0x00, 0x07,
0x01}}, /* 0x1f */
{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
0x00}}, /* 0x20 */
{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
0x00}}, /* 0x21 */
{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
0x00}}, /* 0x22 */
{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
0x00}}, /* 0x23 */
{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
0x00}}, /* 0x24 */
{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
0x00}}, /* 0x25 */
{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
0x00}}, /* 0x26 */
{{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
0x00}}, /* 0x27 */
{{0x43, 0xef, 0xef, 0x87, 0x06, 0x00, 0xd4, 0x1f,
0xa0, 0x83, 0x9f, 0x9f, 0xd5, 0x1f, 0x41, 0x05,
0x63}}, /* 0x28 */
{{0x45, 0xef, 0xef, 0x89, 0x07, 0x01, 0xd9, 0x1f,
0xa0, 0x83, 0x9f, 0x9f, 0xda, 0x1f, 0x41, 0x05,
0x63}}, /* 0x29 */
{{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
0x00}}, /* 0x2a */
{{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
0x00}}, /* 0x2b */
{{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
0x00}}, /* 0x2c */
{{0x59, 0xff, 0xff, 0x9d, 0x17, 0x13, 0x33, 0xba,
0x00, 0x83, 0xff, 0xff, 0x34, 0x0f, 0x41, 0x05,
0x44}}, /* 0x2d */
{{0x5b, 0xff, 0xff, 0x9f, 0x18, 0x14, 0x38, 0xba,
0x00, 0x83, 0xff, 0xff, 0x39, 0x0f, 0x41, 0x05,
0x44}}, /* 0x2e */
{{0x5b, 0xff, 0xff, 0x9f, 0x18, 0x14, 0x3d, 0xba,
0x00, 0x83, 0xff, 0xff, 0x3e, 0x0f, 0x41, 0x05,
0x44}}, /* 0x2f */
{{0x5d, 0xff, 0xff, 0x81, 0x19, 0x95, 0x41, 0xba,
0x00, 0x84, 0xff, 0xff, 0x42, 0x0f, 0x41, 0x05,
0x44}}, /* 0x30 */
{{0x55, 0xff, 0xff, 0x99, 0x0d, 0x0c, 0x3e, 0xba,
0x00, 0x84, 0xff, 0xff, 0x3f, 0x0f, 0x41, 0x05,
0x00}}, /* 0x31 */
{{0x7f, 0x63, 0x63, 0x83, 0x6c, 0x1c, 0x72, 0xba,
0x27, 0x8b, 0xdf, 0xdf, 0x73, 0x00, 0x00, 0x06,
0x01}}, /* 0x32 */
{{0x7f, 0x63, 0x63, 0x83, 0x69, 0x13, 0x6f, 0xba,
0x26, 0x89, 0xdf, 0xdf, 0x6f, 0x00, 0x00, 0x06,
0x01}}, /* 0x33 */
{{0x7f, 0x63, 0x63, 0x82, 0x6b, 0x13, 0x75, 0xba,
0x29, 0x8c, 0xdf, 0xdf, 0x75, 0x00, 0x00, 0x06,
0x01}}, /* 0x34 */
{{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf1,
0xaf, 0x85, 0x3f, 0x3f, 0x25, 0x30, 0x00, 0x02,
0x01}}, /* 0x35 */
{{0x9f, 0x7f, 0x7f, 0x83, 0x85, 0x91, 0x1e, 0xf1,
0xad, 0x81, 0x3f, 0x3f, 0x1f, 0x30, 0x00, 0x02,
0x01}}, /* 0x36 */
{{0xa7, 0x7f, 0x7f, 0x88, 0x89, 0x95, 0x26, 0xf1,
0xb1, 0x85, 0x3f, 0x3f, 0x27, 0x30, 0x00, 0x02,
0x01}}, /* 0x37 */
{{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x28, 0xc4,
0x7a, 0x8e, 0xcf, 0xcf, 0x29, 0x21, 0x00, 0x07,
0x01}}, /* 0x38 */
{{0xce, 0x9f, 0x9f, 0x92, 0xa5, 0x17, 0x28, 0xd4,
0x7a, 0x8e, 0xcf, 0xcf, 0x29, 0x21, 0x00, 0x07,
0x01}}, /* 0x39 */
{{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0x2e, 0xd4,
0x7d, 0x81, 0xcf, 0xcf, 0x2f, 0x21, 0x00, 0x07,
0x01}}, /* 0x3a */
{{0xdc, 0x9f, 0x9f, 0x80, 0xaf, 0x9d, 0xe6, 0xff,
0xc0, 0x83, 0xbf, 0xbf, 0xe7, 0x10, 0x00, 0x07,
0x01}}, /* 0x3b */
{{0x6b, 0x59, 0x59, 0x8f, 0x5e, 0x8c, 0x0b, 0x3e,
0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x05,
0x00}}, /* 0x3c */
{{0x6d, 0x59, 0x59, 0x91, 0x60, 0x89, 0x53, 0xf0,
0x41, 0x84, 0x3f, 0x3f, 0x54, 0x00, 0x00, 0x05,
0x41}}, /* 0x3d */
{{0x86, 0x6a, 0x6a, 0x8a, 0x74, 0x06, 0x8c, 0x15,
0x4f, 0x83, 0xef, 0xef, 0x8d, 0x30, 0x00, 0x02,
0x00}}, /* 0x3e */
{{0x81, 0x6a, 0x6a, 0x85, 0x70, 0x00, 0x0f, 0x3e,
0xeb, 0x8e, 0xdf, 0xdf, 0x10, 0x00, 0x00, 0x02,
0x00}}, /* 0x3f */
{{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x1e, 0xf1,
0xae, 0x85, 0x57, 0x57, 0x1f, 0x30, 0x00, 0x02,
0x01}}, /* 0x40 */
{{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf5,
0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
0x01}}, /* 0x41 */
{{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x20, 0xf5,
0x03, 0x88, 0xff, 0xff, 0x21, 0x10, 0x00, 0x07,
0x01}}, /* 0x42 */
{{0xe6, 0xae, 0xae, 0x8a, 0xbd, 0x90, 0x3d, 0x10,
0x1a, 0x8d, 0x19, 0x19, 0x3e, 0x2f, 0x00, 0x03,
0x00}}, /* 0x43 */
{{0xc3, 0x8f, 0x8f, 0x87, 0x9b, 0x0b, 0x82, 0xef,
0x60, 0x83, 0x5f, 0x5f, 0x83, 0x10, 0x00, 0x07,
0x01}}, /* 0x44 */
{{0x86, 0x69, 0x69, 0x8A, 0x74, 0x06, 0x8C, 0x15,
0x4F, 0x83, 0xEF, 0xEF, 0x8D, 0x30, 0x00, 0x02,
0x00}}, /* 0x45 */
{{0x83, 0x69, 0x69, 0x87, 0x6f, 0x1d, 0x03, 0x3E,
0xE5, 0x8d, 0xDF, 0xe4, 0x04, 0x00, 0x00, 0x06,
0x00}}, /* 0x46 */
{{0x86, 0x6A, 0x6A, 0x8A, 0x74, 0x06, 0x8C, 0x15,
0x4F, 0x83, 0xEF, 0xEF, 0x8D, 0x30, 0x00, 0x02,
0x00}}, /* 0x47 */
{{0x81, 0x6A, 0x6A, 0x85, 0x70, 0x00, 0x0F, 0x3E,
0xEB, 0x8E, 0xDF, 0xDF, 0x10, 0x00, 0x00, 0x02,
0x00}}, /* 0x48 */
{{0xdd, 0xa9, 0xa9, 0x81, 0xb4, 0x97, 0x26, 0xfd,
0x01, 0x8d, 0xff, 0x00, 0x27, 0x10, 0x00, 0x03,
0x01}}, /* 0x49 */
{{0xd9, 0x8f, 0x8f, 0x9d, 0xba, 0x0a, 0x8a, 0xff,
0x60, 0x8b, 0x5f, 0x5f, 0x8b, 0x10, 0x00, 0x03,
0x01}}, /* 0x4a */
{{0xea, 0xae, 0xae, 0x8e, 0xba, 0x82, 0x40, 0x10,
0x1b, 0x87, 0x19, 0x1a, 0x41, 0x0f, 0x00, 0x03,
0x00}}, /* 0x4b */
{{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0xf1, 0xff,
0xc0, 0x83, 0xbf, 0xbf, 0xf2, 0x10, 0x00, 0x07,
0x01}}, /* 0x4c */
{{0x75, 0x5f, 0x5f, 0x99, 0x66, 0x90, 0x53, 0xf0,
0x41, 0x84, 0x3f, 0x3f, 0x54, 0x00, 0x00, 0x05,
0x41}},
{{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0x0b, 0x3e,
0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x00,
0x00}}, /* 0x4e */
{{0xcd, 0x9f, 0x9f, 0x91, 0xab, 0x1c, 0x3a, 0xff,
0x20, 0x83, 0x1f, 0x1f, 0x3b, 0x10, 0x00, 0x07,
0x21}}, /* 0x4f */
{{0x15, 0xd1, 0xd1, 0x99, 0xe2, 0x19, 0x3d, 0x10,
0x1a, 0x8d, 0x19, 0x19, 0x3e, 0x2f, 0x01, 0x0c,
0x20}}, /* 0x50 */
{{0x0e, 0xef, 0xef, 0x92, 0xfe, 0x03, 0x30, 0xf0,
0x1e, 0x83, 0x1b, 0x1c, 0x31, 0x00, 0x01, 0x00,
0x61}}, /* 0x51 */
{{0x85, 0x77, 0x77, 0x89, 0x7d, 0x01, 0x31, 0xf0,
0x1e, 0x84, 0x1b, 0x1c, 0x32, 0x00, 0x00, 0x02,
0x41}}, /* 0x52 */
{{0x87, 0x77, 0x77, 0x8b, 0x81, 0x0b, 0x68, 0xf0,
0x5a, 0x80, 0x57, 0x57, 0x69, 0x00, 0x00, 0x02,
0x01}}, /* 0x53 */
{{0xcd, 0x8f, 0x8f, 0x91, 0x9b, 0x1b, 0x7a, 0xff,
0x64, 0x8c, 0x5f, 0x62, 0x7b, 0x10, 0x00, 0x07,
0x41}} /* 0x54 */
};
static const struct SiS_VCLKData SiSUSB_VCLKData[] = {
{0x1b, 0xe1, 25}, /* 0x00 */
{0x4e, 0xe4, 28}, /* 0x01 */
{0x57, 0xe4, 31}, /* 0x02 */
{0xc3, 0xc8, 36}, /* 0x03 */
{0x42, 0xe2, 40}, /* 0x04 */
{0xfe, 0xcd, 43}, /* 0x05 */
{0x5d, 0xc4, 44}, /* 0x06 */
{0x52, 0xe2, 49}, /* 0x07 */
{0x53, 0xe2, 50}, /* 0x08 */
{0x74, 0x67, 52}, /* 0x09 */
{0x6d, 0x66, 56}, /* 0x0a */
{0x5a, 0x64, 65}, /* 0x0b */
{0x46, 0x44, 67}, /* 0x0c */
{0xb1, 0x46, 68}, /* 0x0d */
{0xd3, 0x4a, 72}, /* 0x0e */
{0x29, 0x61, 75}, /* 0x0f */
{0x6e, 0x46, 76}, /* 0x10 */
{0x2b, 0x61, 78}, /* 0x11 */
{0x31, 0x42, 79}, /* 0x12 */
{0xab, 0x44, 83}, /* 0x13 */
{0x46, 0x25, 84}, /* 0x14 */
{0x78, 0x29, 86}, /* 0x15 */
{0x62, 0x44, 94}, /* 0x16 */
{0x2b, 0x41, 104}, /* 0x17 */
{0x3a, 0x23, 105}, /* 0x18 */
{0x70, 0x44, 108}, /* 0x19 */
{0x3c, 0x23, 109}, /* 0x1a */
{0x5e, 0x43, 113}, /* 0x1b */
{0xbc, 0x44, 116}, /* 0x1c */
{0xe0, 0x46, 132}, /* 0x1d */
{0x54, 0x42, 135}, /* 0x1e */
{0xea, 0x2a, 139}, /* 0x1f */
{0x41, 0x22, 157}, /* 0x20 */
{0x70, 0x24, 162}, /* 0x21 */
{0x30, 0x21, 175}, /* 0x22 */
{0x4e, 0x22, 189}, /* 0x23 */
{0xde, 0x26, 194}, /* 0x24 */
{0x62, 0x06, 202}, /* 0x25 */
{0x3f, 0x03, 229}, /* 0x26 */
{0xb8, 0x06, 234}, /* 0x27 */
{0x34, 0x02, 253}, /* 0x28 */
{0x58, 0x04, 255}, /* 0x29 */
{0x24, 0x01, 265}, /* 0x2a */
{0x9b, 0x02, 267}, /* 0x2b */
{0x70, 0x05, 270}, /* 0x2c */
{0x25, 0x01, 272}, /* 0x2d */
{0x9c, 0x02, 277}, /* 0x2e */
{0x27, 0x01, 286}, /* 0x2f */
{0x3c, 0x02, 291}, /* 0x30 */
{0xef, 0x0a, 292}, /* 0x31 */
{0xf6, 0x0a, 310}, /* 0x32 */
{0x95, 0x01, 315}, /* 0x33 */
{0xf0, 0x09, 324}, /* 0x34 */
{0xfe, 0x0a, 331}, /* 0x35 */
{0xf3, 0x09, 332}, /* 0x36 */
{0xea, 0x08, 340}, /* 0x37 */
{0xe8, 0x07, 376}, /* 0x38 */
{0xde, 0x06, 389}, /* 0x39 */
{0x52, 0x2a, 54}, /* 0x3a 301 TV */
{0x52, 0x6a, 27}, /* 0x3b 301 TV */
{0x62, 0x24, 70}, /* 0x3c 301 TV */
{0x62, 0x64, 70}, /* 0x3d 301 TV */
{0xa8, 0x4c, 30}, /* 0x3e 301 TV */
{0x20, 0x26, 33}, /* 0x3f 301 TV */
{0x31, 0xc2, 39}, /* 0x40 */
{0x60, 0x36, 30}, /* 0x41 Chrontel */
{0x40, 0x4a, 28}, /* 0x42 Chrontel */
{0x9f, 0x46, 44}, /* 0x43 Chrontel */
{0x97, 0x2c, 26}, /* 0x44 */
{0x44, 0xe4, 25}, /* 0x45 Chrontel */
{0x7e, 0x32, 47}, /* 0x46 Chrontel */
{0x8a, 0x24, 31}, /* 0x47 Chrontel */
{0x97, 0x2c, 26}, /* 0x48 Chrontel */
{0xce, 0x3c, 39}, /* 0x49 */
{0x52, 0x4a, 36}, /* 0x4a Chrontel */
{0x34, 0x61, 95}, /* 0x4b */
{0x78, 0x27, 108}, /* 0x4c - was 102 */
{0x66, 0x43, 123}, /* 0x4d Modes 0x26-0x28 (1400x1050) */
{0x41, 0x4e, 21}, /* 0x4e */
{0xa1, 0x4a, 29}, /* 0x4f Chrontel */
{0x19, 0x42, 42}, /* 0x50 */
{0x54, 0x46, 58}, /* 0x51 Chrontel */
{0x25, 0x42, 61}, /* 0x52 */
{0x44, 0x44, 66}, /* 0x53 Chrontel */
{0x3a, 0x62, 70}, /* 0x54 Chrontel */
{0x62, 0xc6, 34}, /* 0x55 848x480-60 */
{0x6a, 0xc6, 37}, /* 0x56 848x480-75 - TEMP */
{0xbf, 0xc8, 35}, /* 0x57 856x480-38i,60 */
{0x30, 0x23, 88}, /* 0x58 1360x768-62 (is 60Hz!) */
{0x52, 0x07, 149}, /* 0x59 1280x960-85 */
{0x56, 0x07, 156}, /* 0x5a 1400x1050-75 */
{0x70, 0x29, 81}, /* 0x5b 1280x768 LCD */
{0x45, 0x25, 83}, /* 0x5c 1280x800 */
{0x70, 0x0a, 147}, /* 0x5d 1680x1050 */
{0x70, 0x24, 162}, /* 0x5e 1600x1200 */
{0x5a, 0x64, 65}, /* 0x5f 1280x720 - temp */
{0x63, 0x46, 68}, /* 0x60 1280x768_2 */
{0x31, 0x42, 79}, /* 0x61 1280x768_3 - temp */
{0, 0, 0}, /* 0x62 - custom (will be filled out at run-time) */
{0x5a, 0x64, 65}, /* 0x63 1280x720 (LCD LVDS) */
{0x70, 0x28, 90}, /* 0x64 1152x864@60 */
{0x41, 0xc4, 32}, /* 0x65 848x480@60 */
{0x5c, 0xc6, 32}, /* 0x66 856x480@60 */
{0x76, 0xe7, 27}, /* 0x67 720x480@60 */
{0x5f, 0xc6, 33}, /* 0x68 720/768x576@60 */
{0x52, 0x27, 75}, /* 0x69 1920x1080i 60Hz interlaced */
{0x7c, 0x6b, 38}, /* 0x6a 960x540@60 */
{0xe3, 0x56, 41}, /* 0x6b 960x600@60 */
{0x45, 0x25, 83}, /* 0x6c 1280x800 */
{0x70, 0x28, 90}, /* 0x6d 1152x864@60 */
{0x15, 0xe1, 20}, /* 0x6e 640x400@60 (fake, not actually used) */
{0x5f, 0xc6, 33}, /* 0x6f 720x576@60 */
{0x37, 0x5a, 10}, /* 0x70 320x200@60 (fake, not actually used) */
{0x2b, 0xc2, 35} /* 0x71 768@576@60 */
};
int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
extern int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data);
extern int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 * data);
extern int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
u8 index, u8 data);
extern int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
u8 index, u8 * data);
extern int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port,
u8 idx, u8 myand, u8 myor);
extern int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port,
u8 index, u8 myor);
extern int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port,
u8 idx, u8 myand);
void sisusb_delete(struct kref *kref);
int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 * data);
int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
u32 dest, int length, size_t * bytes_written);
int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
int sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
u8 * arg, int cmapsz, int ch512, int dorecalc,
struct vc_data *c, int fh, int uplock);
void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location);
int sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last);
void sisusb_console_exit(struct sisusb_usb_data *sisusb);
void sisusb_init_concode(void);
#endif

View File

@@ -0,0 +1,161 @@
/*
* General structure definitions for universal mode switching modules
*
* Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria
*
* If distributed as part of the Linux kernel, the following license terms
* apply:
*
* * 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 named License,
* * or 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*
* Otherwise, the following license terms apply:
*
* * Redistribution and use in source and binary forms, with or without
* * modification, are permitted provided that the following conditions
* * are met:
* * 1) Redistributions of source code must retain the above copyright
* * notice, this list of conditions and the following disclaimer.
* * 2) Redistributions in binary form must reproduce the above copyright
* * notice, this list of conditions and the following disclaimer in the
* * documentation and/or other materials provided with the distribution.
* * 3) The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission.
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Thomas Winischhofer <thomas@winischhofer.net>
*
*/
#ifndef _SISUSB_STRUCT_H_
#define _SISUSB_STRUCT_H_
struct SiS_St {
unsigned char St_ModeID;
unsigned short St_ModeFlag;
unsigned char St_StTableIndex;
unsigned char St_CRT2CRTC;
unsigned char St_ResInfo;
unsigned char VB_StTVFlickerIndex;
unsigned char VB_StTVEdgeIndex;
unsigned char VB_StTVYFilterIndex;
unsigned char St_PDC;
};
struct SiS_StandTable {
unsigned char CRT_COLS;
unsigned char ROWS;
unsigned char CHAR_HEIGHT;
unsigned short CRT_LEN;
unsigned char SR[4];
unsigned char MISC;
unsigned char CRTC[0x19];
unsigned char ATTR[0x14];
unsigned char GRC[9];
};
struct SiS_StResInfo_S {
unsigned short HTotal;
unsigned short VTotal;
};
struct SiS_Ext {
unsigned char Ext_ModeID;
unsigned short Ext_ModeFlag;
unsigned short Ext_VESAID;
unsigned char Ext_RESINFO;
unsigned char VB_ExtTVFlickerIndex;
unsigned char VB_ExtTVEdgeIndex;
unsigned char VB_ExtTVYFilterIndex;
unsigned char VB_ExtTVYFilterIndexROM661;
unsigned char REFindex;
char ROMMODEIDX661;
};
struct SiS_Ext2 {
unsigned short Ext_InfoFlag;
unsigned char Ext_CRT1CRTC;
unsigned char Ext_CRTVCLK;
unsigned char Ext_CRT2CRTC;
unsigned char Ext_CRT2CRTC_NS;
unsigned char ModeID;
unsigned short XRes;
unsigned short YRes;
unsigned char Ext_PDC;
unsigned char Ext_FakeCRT2CRTC;
unsigned char Ext_FakeCRT2Clk;
};
struct SiS_CRT1Table {
unsigned char CR[17];
};
struct SiS_VCLKData {
unsigned char SR2B, SR2C;
unsigned short CLOCK;
};
struct SiS_ModeResInfo {
unsigned short HTotal;
unsigned short VTotal;
unsigned char XChar;
unsigned char YChar;
};
struct SiS_Private {
void *sisusb;
unsigned long IOAddress;
unsigned long SiS_P3c4;
unsigned long SiS_P3d4;
unsigned long SiS_P3c0;
unsigned long SiS_P3ce;
unsigned long SiS_P3c2;
unsigned long SiS_P3ca;
unsigned long SiS_P3c6;
unsigned long SiS_P3c7;
unsigned long SiS_P3c8;
unsigned long SiS_P3c9;
unsigned long SiS_P3cb;
unsigned long SiS_P3cc;
unsigned long SiS_P3cd;
unsigned long SiS_P3da;
unsigned long SiS_Part1Port;
unsigned char SiS_MyCR63;
unsigned short SiS_CRT1Mode;
unsigned short SiS_ModeType;
unsigned short SiS_SetFlag;
const struct SiS_StandTable *SiS_StandTable;
const struct SiS_St *SiS_SModeIDTable;
const struct SiS_Ext *SiS_EModeIDTable;
const struct SiS_Ext2 *SiS_RefIndex;
const struct SiS_CRT1Table *SiS_CRT1Table;
const struct SiS_VCLKData *SiS_VCLKData;
const struct SiS_ModeResInfo *SiS_ModeResInfo;
};
#endif

View File

@@ -0,0 +1,162 @@
/*
* PlayStation 2 Trance Vibrator driver
*
* Copyright (C) 2006 Sam Hocevar <sam@zoy.org>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Standard include files */
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
/* Version Information */
#define DRIVER_VERSION "v1.1"
#define DRIVER_AUTHOR "Sam Hocevar, sam@zoy.org"
#define DRIVER_DESC "PlayStation 2 Trance Vibrator driver"
#define TRANCEVIBRATOR_VENDOR_ID 0x0b49 /* ASCII Corporation */
#define TRANCEVIBRATOR_PRODUCT_ID 0x064f /* Trance Vibrator */
static struct usb_device_id id_table [] = {
{ USB_DEVICE(TRANCEVIBRATOR_VENDOR_ID, TRANCEVIBRATOR_PRODUCT_ID) },
{ },
};
MODULE_DEVICE_TABLE (usb, id_table);
/* Driver-local specific stuff */
struct trancevibrator {
struct usb_device *udev;
unsigned int speed;
};
static ssize_t show_speed(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct trancevibrator *tv = usb_get_intfdata(intf);
return sprintf(buf, "%d\n", tv->speed);
}
static ssize_t set_speed(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct trancevibrator *tv = usb_get_intfdata(intf);
int temp, retval, old;
temp = simple_strtoul(buf, NULL, 10);
if (temp > 255)
temp = 255;
else if (temp < 0)
temp = 0;
old = tv->speed;
tv->speed = temp;
dev_dbg(&tv->udev->dev, "speed = %d\n", tv->speed);
/* Set speed */
retval = usb_control_msg(tv->udev, usb_sndctrlpipe(tv->udev, 0),
0x01, /* vendor request: set speed */
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
tv->speed, /* speed value */
0, NULL, 0, USB_CTRL_GET_TIMEOUT);
if (retval) {
tv->speed = old;
dev_dbg(&tv->udev->dev, "retval = %d\n", retval);
return retval;
}
return count;
}
static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR, show_speed, set_speed);
static int tv_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct trancevibrator *dev;
int retval;
dev = kzalloc(sizeof(struct trancevibrator), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
retval = -ENOMEM;
goto error;
}
dev->udev = usb_get_dev(udev);
usb_set_intfdata(interface, dev);
retval = device_create_file(&interface->dev, &dev_attr_speed);
if (retval)
goto error_create_file;
return 0;
error_create_file:
usb_put_dev(udev);
usb_set_intfdata(interface, NULL);
error:
kfree(dev);
return retval;
}
static void tv_disconnect(struct usb_interface *interface)
{
struct trancevibrator *dev;
dev = usb_get_intfdata (interface);
device_remove_file(&interface->dev, &dev_attr_speed);
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
}
/* USB subsystem object */
static struct usb_driver tv_driver = {
.name = "trancevibrator",
.probe = tv_probe,
.disconnect = tv_disconnect,
.id_table = id_table,
};
static int __init tv_init(void)
{
int retval = usb_register(&tv_driver);
if (retval) {
err("usb_register failed. Error number %d", retval);
return retval;
}
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return 0;
}
static void __exit tv_exit(void)
{
usb_deregister(&tv_driver);
}
module_init (tv_init);
module_exit (tv_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,101 @@
/*
* Common Header File for the Elan Digital Systems U132 adapter
* this file should be included by both the "ftdi-u132" and
* the "u132-hcd" modules.
*
* Copyright(C) 2006 Elan Digital Systems Limited
*(http://www.elandigitalsystems.com)
*
* Author and Maintainer - Tony Olech - Elan Digital Systems
*(tony.olech@elandigitalsystems.com)
*
* 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, version 2.
*
*
* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
* based on various USB client drivers in the 2.6.15 linux kernel
* with constant reference to the 3rd Edition of Linux Device Drivers
* published by O'Reilly
*
* The U132 adapter is a USB to CardBus adapter specifically designed
* for PC cards that contain an OHCI host controller. Typical PC cards
* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
*
* The U132 adapter will *NOT *work with PC cards that do not contain
* an OHCI controller. A simple way to test whether a PC card has an
* OHCI controller as an interface is to insert the PC card directly
* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
* then there is a good chance that the U132 adapter will support the
* PC card.(you also need the specific client driver for the PC card)
*
* Please inform the Author and Maintainer about any PC cards that
* contain OHCI Host Controller and work when directly connected to
* an embedded CardBus slot but do not work when they are connected
* via an ELAN U132 adapter.
*
* The driver consists of two modules, the "ftdi-u132" module is
* a USB client driver that interfaces to the FTDI chip within
* the U132 adapter manufactured by Elan Digital Systems, and the
* "u132-hcd" module is a USB host controller driver that talks
* to the OHCI controller within CardBus card that are inserted
* in the U132 adapter.
*
* The "ftdi-u132" module should be loaded automatically by the
* hot plug system when the U132 adapter is plugged in. The module
* initialises the adapter which mostly consists of synchronising
* the FTDI chip, before continuously polling the adapter to detect
* PC card insertions. As soon as a PC card containing a recognised
* OHCI controller is seen the "ftdi-u132" module explicitly requests
* the kernel to load the "u132-hcd" module.
*
* The "ftdi-u132" module provides the interface to the inserted
* PC card and the "u132-hcd" module uses the API to send and receive
* data. The API features call-backs, so that part of the "u132-hcd"
* module code will run in the context of one of the kernel threads
* of the "ftdi-u132" module.
*
*/
int ftdi_elan_switch_on_diagnostics(int number);
void ftdi_elan_gone_away(struct platform_device *pdev);
void start_usb_lock_device_tracing(void);
struct u132_platform_data {
u16 vendor;
u16 device;
u8 potpg;
void (*port_power) (struct device *dev, int is_on);
void (*reset) (struct device *dev);
};
int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
int toggle_bits, int error_count, int condition_code, int repeat_number,
int halted, int skipped, int actual, int non_null));
int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
int toggle_bits, int error_count, int condition_code, int repeat_number,
int halted, int skipped, int actual, int non_null));
int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
int toggle_bits, int error_count, int condition_code, int repeat_number,
int halted, int skipped, int actual, int non_null));
int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
int toggle_bits, int error_count, int condition_code, int repeat_number,
int halted, int skipped, int actual, int non_null));
int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
int toggle_bits, int error_count, int condition_code, int repeat_number,
int halted, int skipped, int actual, int non_null));
int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
void *endp);
int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset,
u8 width, u32 *data);
int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset,
u8 width, u32 data);

View File

@@ -0,0 +1,454 @@
/*****************************************************************************
* USBLCD Kernel Driver *
* Version 1.05 *
* (C) 2005 Georges Toth <g.toth@e-biz.lu> *
* *
* This file is licensed under the GPL. See COPYING in the package. *
* Based on usb-skeleton.c 2.0 by Greg Kroah-Hartman (greg@kroah.com) *
* *
* *
* 28.02.05 Complete rewrite of the original usblcd.c driver, *
* based on usb_skeleton.c. *
* This new driver allows more than one USB-LCD to be connected *
* and controlled, at once *
*****************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
#define DRIVER_VERSION "USBLCD Driver Version 1.05"
#define USBLCD_MINOR 144
#define IOCTL_GET_HARD_VERSION 1
#define IOCTL_GET_DRV_VERSION 2
static struct usb_device_id id_table [] = {
{ .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
{ },
};
MODULE_DEVICE_TABLE (usb, id_table);
static DEFINE_MUTEX(open_disc_mutex);
struct usb_lcd {
struct usb_device * udev; /* init: probe_lcd */
struct usb_interface * interface; /* the interface for this device */
unsigned char * bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
struct kref kref;
struct semaphore limit_sem; /* to stop writes at full throttle from
* using up all RAM */
struct usb_anchor submitted; /* URBs to wait for before suspend */
};
#define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
#define USB_LCD_CONCURRENT_WRITES 5
static struct usb_driver lcd_driver;
static void lcd_delete(struct kref *kref)
{
struct usb_lcd *dev = to_lcd_dev(kref);
usb_put_dev(dev->udev);
kfree (dev->bulk_in_buffer);
kfree (dev);
}
static int lcd_open(struct inode *inode, struct file *file)
{
struct usb_lcd *dev;
struct usb_interface *interface;
int subminor, r;
subminor = iminor(inode);
interface = usb_find_interface(&lcd_driver, subminor);
if (!interface) {
err ("USBLCD: %s - error, can't find device for minor %d",
__func__, subminor);
return -ENODEV;
}
mutex_lock(&open_disc_mutex);
dev = usb_get_intfdata(interface);
if (!dev) {
mutex_unlock(&open_disc_mutex);
return -ENODEV;
}
/* increment our usage count for the device */
kref_get(&dev->kref);
mutex_unlock(&open_disc_mutex);
/* grab a power reference */
r = usb_autopm_get_interface(interface);
if (r < 0) {
kref_put(&dev->kref, lcd_delete);
return r;
}
/* save our object in the file's private structure */
file->private_data = dev;
return 0;
}
static int lcd_release(struct inode *inode, struct file *file)
{
struct usb_lcd *dev;
dev = (struct usb_lcd *)file->private_data;
if (dev == NULL)
return -ENODEV;
/* decrement the count on our device */
usb_autopm_put_interface(dev->interface);
kref_put(&dev->kref, lcd_delete);
return 0;
}
static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos)
{
struct usb_lcd *dev;
int retval = 0;
int bytes_read;
dev = (struct usb_lcd *)file->private_data;
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&bytes_read, 10000);
/* if the read was successful, copy the data to userspace */
if (!retval) {
if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
retval = -EFAULT;
else
retval = bytes_read;
}
return retval;
}
static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct usb_lcd *dev;
u16 bcdDevice;
char buf[30];
dev = (struct usb_lcd *)file->private_data;
if (dev == NULL)
return -ENODEV;
switch (cmd) {
case IOCTL_GET_HARD_VERSION:
lock_kernel();
bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
sprintf(buf,"%1d%1d.%1d%1d",
(bcdDevice & 0xF000)>>12,
(bcdDevice & 0xF00)>>8,
(bcdDevice & 0xF0)>>4,
(bcdDevice & 0xF));
unlock_kernel();
if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
return -EFAULT;
break;
case IOCTL_GET_DRV_VERSION:
sprintf(buf,DRIVER_VERSION);
if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
return -EFAULT;
break;
default:
return -ENOTTY;
break;
}
return 0;
}
static void lcd_write_bulk_callback(struct urb *urb)
{
struct usb_lcd *dev;
int status = urb->status;
dev = urb->context;
/* sync/async unlink faults aren't errors */
if (status &&
!(status == -ENOENT ||
status == -ECONNRESET ||
status == -ESHUTDOWN)) {
dbg("USBLCD: %s - nonzero write bulk status received: %d",
__func__, status);
}
/* free up our allocated buffer */
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
up(&dev->limit_sem);
}
static ssize_t lcd_write(struct file *file, const char __user * user_buffer, size_t count, loff_t *ppos)
{
struct usb_lcd *dev;
int retval = 0, r;
struct urb *urb = NULL;
char *buf = NULL;
dev = (struct usb_lcd *)file->private_data;
/* verify that we actually have some data to write */
if (count == 0)
goto exit;
r = down_interruptible(&dev->limit_sem);
if (r < 0)
return -EINTR;
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto err_no_buf;
}
buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}
/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, lcd_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &dev->submitted);
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
err("USBLCD: %s - failed submitting write urb, error %d", __func__, retval);
goto error_unanchor;
}
/* release our reference to this urb, the USB core will eventually free it entirely */
usb_free_urb(urb);
exit:
return count;
error_unanchor:
usb_unanchor_urb(urb);
error:
usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
usb_free_urb(urb);
err_no_buf:
up(&dev->limit_sem);
return retval;
}
static const struct file_operations lcd_fops = {
.owner = THIS_MODULE,
.read = lcd_read,
.write = lcd_write,
.open = lcd_open,
.unlocked_ioctl = lcd_ioctl,
.release = lcd_release,
};
/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with the driver core
*/
static struct usb_class_driver lcd_class = {
.name = "lcd%d",
.fops = &lcd_fops,
.minor_base = USBLCD_MINOR,
};
static int lcd_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_lcd *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
int i;
int retval = -ENOMEM;
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
err("Out of memory");
goto error;
}
kref_init(&dev->kref);
sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
init_usb_anchor(&dev->submitted);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) {
dev_warn(&interface->dev, "USBLCD model not supported.\n");
retval = -ENODEV;
goto error;
}
/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
err("Could not allocate bulk_in_buffer");
goto error;
}
}
if (!dev->bulk_out_endpointAddr &&
usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
err("Could not find both bulk-in and bulk-out endpoints");
goto error;
}
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &lcd_class);
if (retval) {
/* something prevented us from registering this driver */
err("Not able to get a minor for this device.");
usb_set_intfdata(interface, NULL);
goto error;
}
i = le16_to_cpu(dev->udev->descriptor.bcdDevice);
dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found "
"at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8,
(i & 0xF0)>>4,(i & 0xF), dev->udev->devnum);
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n",
interface->minor);
return 0;
error:
if (dev)
kref_put(&dev->kref, lcd_delete);
return retval;
}
static void lcd_draw_down(struct usb_lcd *dev)
{
int time;
time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
if (!time)
usb_kill_anchored_urbs(&dev->submitted);
}
static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_lcd *dev = usb_get_intfdata(intf);
if (!dev)
return 0;
lcd_draw_down(dev);
return 0;
}
static int lcd_resume (struct usb_interface *intf)
{
return 0;
}
static void lcd_disconnect(struct usb_interface *interface)
{
struct usb_lcd *dev;
int minor = interface->minor;
mutex_lock(&open_disc_mutex);
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
mutex_unlock(&open_disc_mutex);
/* give back our minor */
usb_deregister_dev(interface, &lcd_class);
/* decrement our usage count */
kref_put(&dev->kref, lcd_delete);
dev_info(&interface->dev, "USB LCD #%d now disconnected\n", minor);
}
static struct usb_driver lcd_driver = {
.name = "usblcd",
.probe = lcd_probe,
.disconnect = lcd_disconnect,
.suspend = lcd_suspend,
.resume = lcd_resume,
.id_table = id_table,
.supports_autosuspend = 1,
};
static int __init usb_lcd_init(void)
{
int result;
result = usb_register(&lcd_driver);
if (result)
err("usb_register failed. Error number %d", result);
return result;
}
static void __exit usb_lcd_exit(void)
{
usb_deregister(&lcd_driver);
}
module_init(usb_lcd_init);
module_exit(usb_lcd_exit);
MODULE_AUTHOR("Georges Toth <g.toth@e-biz.lu>");
MODULE_DESCRIPTION(DRIVER_VERSION);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,189 @@
/*
* USB LED driver - 1.1
*
* Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com)
*
* 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, version 2.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
#define DRIVER_DESC "USB LED Driver"
#define VENDOR_ID 0x0fc5
#define PRODUCT_ID 0x1223
/* table of devices that work with this driver */
static struct usb_device_id id_table [] = {
{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
{ },
};
MODULE_DEVICE_TABLE (usb, id_table);
struct usb_led {
struct usb_device * udev;
unsigned char blue;
unsigned char red;
unsigned char green;
};
#define BLUE 0x04
#define RED 0x02
#define GREEN 0x01
static void change_color(struct usb_led *led)
{
int retval;
unsigned char color = 0x07;
unsigned char *buffer;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&led->udev->dev, "out of memory\n");
return;
}
if (led->blue)
color &= ~(BLUE);
if (led->red)
color &= ~(RED);
if (led->green)
color &= ~(GREEN);
dev_dbg(&led->udev->dev,
"blue = %d, red = %d, green = %d, color = %.2x\n",
led->blue, led->red, led->green, color);
retval = usb_control_msg(led->udev,
usb_sndctrlpipe(led->udev, 0),
0x12,
0xc8,
(0x02 * 0x100) + 0x0a,
(0x00 * 0x100) + color,
buffer,
8,
2000);
if (retval)
dev_dbg(&led->udev->dev, "retval = %d\n", retval);
kfree(buffer);
}
#define show_set(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_led *led = usb_get_intfdata(intf); \
\
return sprintf(buf, "%d\n", led->value); \
} \
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_led *led = usb_get_intfdata(intf); \
int temp = simple_strtoul(buf, NULL, 10); \
\
led->value = temp; \
change_color(led); \
return count; \
} \
static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value);
show_set(blue);
show_set(red);
show_set(green);
static int led_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_led *dev = NULL;
int retval = -ENOMEM;
dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
goto error_mem;
}
dev->udev = usb_get_dev(udev);
usb_set_intfdata (interface, dev);
retval = device_create_file(&interface->dev, &dev_attr_blue);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_red);
if (retval)
goto error;
retval = device_create_file(&interface->dev, &dev_attr_green);
if (retval)
goto error;
dev_info(&interface->dev, "USB LED device now attached\n");
return 0;
error:
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
usb_set_intfdata (interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
error_mem:
return retval;
}
static void led_disconnect(struct usb_interface *interface)
{
struct usb_led *dev;
dev = usb_get_intfdata (interface);
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
/* first remove the files, then set the pointer to NULL */
usb_set_intfdata (interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
dev_info(&interface->dev, "USB LED now disconnected\n");
}
static struct usb_driver led_driver = {
.name = "usbled",
.probe = led_probe,
.disconnect = led_disconnect,
.id_table = id_table,
};
static int __init usb_led_init(void)
{
int retval = 0;
retval = usb_register(&led_driver);
if (retval)
err("usb_register failed. Error number %d", retval);
return retval;
}
static void __exit usb_led_exit(void)
{
usb_deregister(&led_driver);
}
module_init (usb_led_init);
module_exit (usb_led_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,449 @@
/*
* USB 7 Segment Driver
*
* Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com>
* Based on usbled.c by Greg Kroah-Hartman (greg@kroah.com)
*
* 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, version 2.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/usb.h>
#define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>"
#define DRIVER_DESC "USB 7 Segment Driver"
#define VENDOR_ID 0x0fc5
#define PRODUCT_ID 0x1227
#define MAXLEN 6
/* table of devices that work with this driver */
static struct usb_device_id id_table[] = {
{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
/* the different text display modes the device is capable of */
static char *display_textmodes[] = {"raw", "hex", "ascii", NULL};
struct usb_sevsegdev {
struct usb_device *udev;
struct usb_interface *intf;
u8 powered;
u8 mode_msb;
u8 mode_lsb;
u8 decimals[MAXLEN];
u8 textmode;
u8 text[MAXLEN];
u16 textlength;
u8 shadow_power; /* for PM */
};
/* sysfs_streq can't replace this completely
* If the device was in hex mode, and the user wanted a 0,
* if str commands are used, we would assume the end of string
* so mem commands are used.
*/
inline size_t my_memlen(const char *buf, size_t count)
{
if (count > 0 && buf[count-1] == '\n')
return count - 1;
else
return count;
}
static void update_display_powered(struct usb_sevsegdev *mydev)
{
int rc;
if (!mydev->shadow_power && mydev->powered) {
rc = usb_autopm_get_interface(mydev->intf);
if (rc < 0)
return;
}
rc = usb_control_msg(mydev->udev,
usb_sndctrlpipe(mydev->udev, 0),
0x12,
0x48,
(80 * 0x100) + 10, /* (power mode) */
(0x00 * 0x100) + (mydev->powered ? 1 : 0),
NULL,
0,
2000);
if (rc < 0)
dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc);
if (mydev->shadow_power && !mydev->powered)
usb_autopm_put_interface(mydev->intf);
}
static void update_display_mode(struct usb_sevsegdev *mydev)
{
int rc;
if(mydev->shadow_power != 1)
return;
rc = usb_control_msg(mydev->udev,
usb_sndctrlpipe(mydev->udev, 0),
0x12,
0x48,
(82 * 0x100) + 10, /* (set mode) */
(mydev->mode_msb * 0x100) + mydev->mode_lsb,
NULL,
0,
2000);
if (rc < 0)
dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc);
}
static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
{
int rc;
int i;
unsigned char *buffer;
u8 decimals = 0;
if(mydev->shadow_power != 1)
return;
buffer = kzalloc(MAXLEN, mf);
if (!buffer) {
dev_err(&mydev->udev->dev, "out of memory\n");
return;
}
/* The device is right to left, where as you write left to right */
for (i = 0; i < mydev->textlength; i++)
buffer[i] = mydev->text[mydev->textlength-1-i];
rc = usb_control_msg(mydev->udev,
usb_sndctrlpipe(mydev->udev, 0),
0x12,
0x48,
(85 * 0x100) + 10, /* (write text) */
(0 * 0x100) + mydev->textmode, /* mode */
buffer,
mydev->textlength,
2000);
if (rc < 0)
dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc);
kfree(buffer);
/* The device is right to left, where as you write left to right */
for (i = 0; i < sizeof(mydev->decimals); i++)
decimals |= mydev->decimals[i] << i;
rc = usb_control_msg(mydev->udev,
usb_sndctrlpipe(mydev->udev, 0),
0x12,
0x48,
(86 * 0x100) + 10, /* (set decimal) */
(0 * 0x100) + decimals, /* decimals */
NULL,
0,
2000);
if (rc < 0)
dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc);
}
#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn) \
static ssize_t show_attr_##name(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \
\
return sprintf(buf, "%u\n", mydev->name); \
} \
\
static ssize_t set_attr_##name(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \
\
mydev->name = simple_strtoul(buf, NULL, 10); \
update_fcn(mydev); \
\
return count; \
} \
static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_attr_##name, set_attr_##name);
static ssize_t show_attr_text(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
return snprintf(buf, mydev->textlength, "%s\n", mydev->text);
}
static ssize_t set_attr_text(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
size_t end = my_memlen(buf, count);
if (end > sizeof(mydev->text))
return -EINVAL;
memset(mydev->text, 0, sizeof(mydev->text));
mydev->textlength = end;
if (end > 0)
memcpy(mydev->text, buf, end);
update_display_visual(mydev, GFP_KERNEL);
return count;
}
static DEVICE_ATTR(text, S_IRUGO | S_IWUSR, show_attr_text, set_attr_text);
static ssize_t show_attr_decimals(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
int i;
int pos;
for (i = 0; i < sizeof(mydev->decimals); i++) {
pos = sizeof(mydev->decimals) - 1 - i;
if (mydev->decimals[i] == 0)
buf[pos] = '0';
else if (mydev->decimals[i] == 1)
buf[pos] = '1';
else
buf[pos] = 'x';
}
buf[sizeof(mydev->decimals)] = '\n';
return sizeof(mydev->decimals) + 1;
}
static ssize_t set_attr_decimals(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
size_t end = my_memlen(buf, count);
int i;
if (end > sizeof(mydev->decimals))
return -EINVAL;
for (i = 0; i < end; i++)
if (buf[i] != '0' && buf[i] != '1')
return -EINVAL;
memset(mydev->decimals, 0, sizeof(mydev->decimals));
for (i = 0; i < end; i++)
if (buf[i] == '1')
mydev->decimals[end-1-i] = 1;
update_display_visual(mydev, GFP_KERNEL);
return count;
}
static DEVICE_ATTR(decimals, S_IRUGO | S_IWUSR, show_attr_decimals, set_attr_decimals);
static ssize_t show_attr_textmode(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
int i;
buf[0] = 0;
for (i = 0; display_textmodes[i]; i++) {
if (mydev->textmode == i) {
strcat(buf, " [");
strcat(buf, display_textmodes[i]);
strcat(buf, "] ");
} else {
strcat(buf, " ");
strcat(buf, display_textmodes[i]);
strcat(buf, " ");
}
}
strcat(buf, "\n");
return strlen(buf);
}
static ssize_t set_attr_textmode(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
int i;
for (i = 0; display_textmodes[i]; i++) {
if (sysfs_streq(display_textmodes[i], buf)) {
mydev->textmode = i;
update_display_visual(mydev, GFP_KERNEL);
return count;
}
}
return -EINVAL;
}
static DEVICE_ATTR(textmode, S_IRUGO | S_IWUSR, show_attr_textmode, set_attr_textmode);
MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
static struct attribute *dev_attrs[] = {
&dev_attr_powered.attr,
&dev_attr_text.attr,
&dev_attr_textmode.attr,
&dev_attr_decimals.attr,
&dev_attr_mode_msb.attr,
&dev_attr_mode_lsb.attr,
NULL
};
static struct attribute_group dev_attr_grp = {
.attrs = dev_attrs,
};
static int sevseg_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_sevsegdev *mydev = NULL;
int rc = -ENOMEM;
mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
if (mydev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
goto error_mem;
}
mydev->udev = usb_get_dev(udev);
mydev->intf = interface;
usb_set_intfdata(interface, mydev);
/*set defaults */
mydev->textmode = 0x02; /* ascii mode */
mydev->mode_msb = 0x06; /* 6 characters */
mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp);
if (rc)
goto error;
dev_info(&interface->dev, "USB 7 Segment device now attached\n");
return 0;
error:
usb_set_intfdata(interface, NULL);
usb_put_dev(mydev->udev);
kfree(mydev);
error_mem:
return rc;
}
static void sevseg_disconnect(struct usb_interface *interface)
{
struct usb_sevsegdev *mydev;
mydev = usb_get_intfdata(interface);
sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp);
usb_set_intfdata(interface, NULL);
usb_put_dev(mydev->udev);
kfree(mydev);
dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
}
static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_sevsegdev *mydev;
mydev = usb_get_intfdata(intf);
mydev->shadow_power = 0;
return 0;
}
static int sevseg_resume(struct usb_interface *intf)
{
struct usb_sevsegdev *mydev;
mydev = usb_get_intfdata(intf);
mydev->shadow_power = 1;
update_display_mode(mydev);
update_display_visual(mydev, GFP_NOIO);
return 0;
}
static int sevseg_reset_resume(struct usb_interface *intf)
{
struct usb_sevsegdev *mydev;
mydev = usb_get_intfdata(intf);
mydev->shadow_power = 1;
update_display_mode(mydev);
update_display_visual(mydev, GFP_NOIO);
return 0;
}
static struct usb_driver sevseg_driver = {
.name = "usbsevseg",
.probe = sevseg_probe,
.disconnect = sevseg_disconnect,
.suspend = sevseg_suspend,
.resume = sevseg_resume,
.reset_resume = sevseg_reset_resume,
.id_table = id_table,
.supports_autosuspend = 1,
};
static int __init usb_sevseg_init(void)
{
int rc = 0;
rc = usb_register(&sevseg_driver);
if (rc)
err("usb_register failed. Error number %d", rc);
return rc;
}
static void __exit usb_sevseg_exit(void)
{
usb_deregister(&sevseg_driver);
}
module_init(usb_sevseg_init);
module_exit(usb_sevseg_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,825 @@
/*****************************************************************************/
/*
* uss720.c -- USS720 USB Parport Cable.
*
* Copyright (C) 1999, 2005, 2010
* Thomas Sailer (t.sailer@alumni.ethz.ch)
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Based on parport_pc.c
*
* History:
* 0.1 04.08.1999 Created
* 0.2 07.08.1999 Some fixes mainly suggested by Tim Waugh
* Interrupt handling currently disabled because
* usb_request_irq crashes somewhere within ohci.c
* for no apparent reason (that is for me, anyway)
* ECP currently untested
* 0.3 10.08.1999 fixing merge errors
* 0.4 13.08.1999 Added Vendor/Product ID of Brad Hard's cable
* 0.5 20.09.1999 usb_control_msg wrapper used
* Nov01.2000 usb_device_table support by Adam J. Richter
* 08.04.2001 Identify version on module load. gb
* 0.6 02.09.2005 Fix "scheduling in interrupt" problem by making save/restore
* context asynchronous
*
*/
/*****************************************************************************/
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/parport.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/kref.h>
/*
* Version Information
*/
#define DRIVER_VERSION "v0.6"
#define DRIVER_AUTHOR "Thomas M. Sailer, t.sailer@alumni.ethz.ch"
#define DRIVER_DESC "USB Parport Cable driver for Cables using the Lucent Technologies USS720 Chip"
/* --------------------------------------------------------------------- */
struct parport_uss720_private {
struct usb_device *usbdev;
struct parport *pp;
struct kref ref_count;
__u8 reg[7]; /* USB registers */
struct list_head asynclist;
spinlock_t asynclock;
};
struct uss720_async_request {
struct parport_uss720_private *priv;
struct kref ref_count;
struct list_head asynclist;
struct completion compl;
struct urb *urb;
struct usb_ctrlrequest dr;
__u8 reg[7];
};
/* --------------------------------------------------------------------- */
static void destroy_priv(struct kref *kref)
{
struct parport_uss720_private *priv = container_of(kref, struct parport_uss720_private, ref_count);
usb_put_dev(priv->usbdev);
kfree(priv);
dbg("destroying priv datastructure");
}
static void destroy_async(struct kref *kref)
{
struct uss720_async_request *rq = container_of(kref, struct uss720_async_request, ref_count);
struct parport_uss720_private *priv = rq->priv;
unsigned long flags;
if (likely(rq->urb))
usb_free_urb(rq->urb);
spin_lock_irqsave(&priv->asynclock, flags);
list_del_init(&rq->asynclist);
spin_unlock_irqrestore(&priv->asynclock, flags);
kfree(rq);
kref_put(&priv->ref_count, destroy_priv);
}
/* --------------------------------------------------------------------- */
static void async_complete(struct urb *urb)
{
struct uss720_async_request *rq;
struct parport *pp;
struct parport_uss720_private *priv;
int status = urb->status;
rq = urb->context;
priv = rq->priv;
pp = priv->pp;
if (status) {
err("async_complete: urb error %d", status);
} else if (rq->dr.bRequest == 3) {
memcpy(priv->reg, rq->reg, sizeof(priv->reg));
#if 0
dbg("async_complete regs %02x %02x %02x %02x %02x %02x %02x",
(unsigned int)priv->reg[0], (unsigned int)priv->reg[1], (unsigned int)priv->reg[2],
(unsigned int)priv->reg[3], (unsigned int)priv->reg[4], (unsigned int)priv->reg[5],
(unsigned int)priv->reg[6]);
#endif
/* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */
if (rq->reg[2] & rq->reg[1] & 0x10 && pp)
parport_generic_irq(pp);
}
complete(&rq->compl);
kref_put(&rq->ref_count, destroy_async);
}
static struct uss720_async_request *submit_async_request(struct parport_uss720_private *priv,
__u8 request, __u8 requesttype, __u16 value, __u16 index,
gfp_t mem_flags)
{
struct usb_device *usbdev;
struct uss720_async_request *rq;
unsigned long flags;
int ret;
if (!priv)
return NULL;
usbdev = priv->usbdev;
if (!usbdev)
return NULL;
rq = kmalloc(sizeof(struct uss720_async_request), mem_flags);
if (!rq) {
err("submit_async_request out of memory");
return NULL;
}
kref_init(&rq->ref_count);
INIT_LIST_HEAD(&rq->asynclist);
init_completion(&rq->compl);
kref_get(&priv->ref_count);
rq->priv = priv;
rq->urb = usb_alloc_urb(0, mem_flags);
if (!rq->urb) {
kref_put(&rq->ref_count, destroy_async);
err("submit_async_request out of memory");
return NULL;
}
rq->dr.bRequestType = requesttype;
rq->dr.bRequest = request;
rq->dr.wValue = cpu_to_le16(value);
rq->dr.wIndex = cpu_to_le16(index);
rq->dr.wLength = cpu_to_le16((request == 3) ? sizeof(rq->reg) : 0);
usb_fill_control_urb(rq->urb, usbdev, (requesttype & 0x80) ? usb_rcvctrlpipe(usbdev, 0) : usb_sndctrlpipe(usbdev, 0),
(unsigned char *)&rq->dr,
(request == 3) ? rq->reg : NULL, (request == 3) ? sizeof(rq->reg) : 0, async_complete, rq);
/* rq->urb->transfer_flags |= URB_ASYNC_UNLINK; */
spin_lock_irqsave(&priv->asynclock, flags);
list_add_tail(&rq->asynclist, &priv->asynclist);
spin_unlock_irqrestore(&priv->asynclock, flags);
kref_get(&rq->ref_count);
ret = usb_submit_urb(rq->urb, mem_flags);
if (!ret)
return rq;
destroy_async(&rq->ref_count);
err("submit_async_request submit_urb failed with %d", ret);
return NULL;
}
static unsigned int kill_all_async_requests_priv(struct parport_uss720_private *priv)
{
struct uss720_async_request *rq;
unsigned long flags;
unsigned int ret = 0;
spin_lock_irqsave(&priv->asynclock, flags);
list_for_each_entry(rq, &priv->asynclist, asynclist) {
usb_unlink_urb(rq->urb);
ret++;
}
spin_unlock_irqrestore(&priv->asynclock, flags);
return ret;
}
/* --------------------------------------------------------------------- */
static int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val, gfp_t mem_flags)
{
struct parport_uss720_private *priv;
struct uss720_async_request *rq;
static const unsigned char regindex[9] = {
4, 0, 1, 5, 5, 0, 2, 3, 6
};
int ret;
if (!pp)
return -EIO;
priv = pp->private_data;
rq = submit_async_request(priv, 3, 0xc0, ((unsigned int)reg) << 8, 0, mem_flags);
if (!rq) {
err("get_1284_register(%u) failed", (unsigned int)reg);
return -EIO;
}
if (!val) {
kref_put(&rq->ref_count, destroy_async);
return 0;
}
if (wait_for_completion_timeout(&rq->compl, HZ)) {
ret = rq->urb->status;
*val = priv->reg[(reg >= 9) ? 0 : regindex[reg]];
if (ret)
printk(KERN_WARNING "get_1284_register: "
"usb error %d\n", ret);
kref_put(&rq->ref_count, destroy_async);
return ret;
}
printk(KERN_WARNING "get_1284_register timeout\n");
kill_all_async_requests_priv(priv);
return -EIO;
}
static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val, gfp_t mem_flags)
{
struct parport_uss720_private *priv;
struct uss720_async_request *rq;
if (!pp)
return -EIO;
priv = pp->private_data;
rq = submit_async_request(priv, 4, 0x40, (((unsigned int)reg) << 8) | val, 0, mem_flags);
if (!rq) {
err("set_1284_register(%u,%u) failed", (unsigned int)reg, (unsigned int)val);
return -EIO;
}
kref_put(&rq->ref_count, destroy_async);
return 0;
}
/* --------------------------------------------------------------------- */
/* ECR modes */
#define ECR_SPP 00
#define ECR_PS2 01
#define ECR_PPF 02
#define ECR_ECP 03
#define ECR_EPP 04
/* Safely change the mode bits in the ECR */
static int change_mode(struct parport *pp, int m)
{
struct parport_uss720_private *priv = pp->private_data;
int mode;
__u8 reg;
if (get_1284_register(pp, 6, &reg, GFP_KERNEL))
return -EIO;
/* Bits <7:5> contain the mode. */
mode = (priv->reg[2] >> 5) & 0x7;
if (mode == m)
return 0;
/* We have to go through mode 000 or 001 */
if (mode > ECR_PS2 && m > ECR_PS2)
if (change_mode(pp, ECR_PS2))
return -EIO;
if (m <= ECR_PS2 && !(priv->reg[1] & 0x20)) {
/* This mode resets the FIFO, so we may
* have to wait for it to drain first. */
unsigned long expire = jiffies + pp->physport->cad->timeout;
switch (mode) {
case ECR_PPF: /* Parallel Port FIFO mode */
case ECR_ECP: /* ECP Parallel Port mode */
/* Poll slowly. */
for (;;) {
if (get_1284_register(pp, 6, &reg, GFP_KERNEL))
return -EIO;
if (priv->reg[2] & 0x01)
break;
if (time_after_eq (jiffies, expire))
/* The FIFO is stuck. */
return -EBUSY;
msleep_interruptible(10);
if (signal_pending (current))
break;
}
}
}
/* Set the mode. */
if (set_1284_register(pp, 6, m << 5, GFP_KERNEL))
return -EIO;
if (get_1284_register(pp, 6, &reg, GFP_KERNEL))
return -EIO;
return 0;
}
/*
* Clear TIMEOUT BIT in EPP MODE
*/
static int clear_epp_timeout(struct parport *pp)
{
unsigned char stat;
if (get_1284_register(pp, 1, &stat, GFP_KERNEL))
return 1;
return stat & 1;
}
/*
* Access functions.
*/
#if 0
static int uss720_irq(int usbstatus, void *buffer, int len, void *dev_id)
{
struct parport *pp = (struct parport *)dev_id;
struct parport_uss720_private *priv = pp->private_data;
if (usbstatus != 0 || len < 4 || !buffer)
return 1;
memcpy(priv->reg, buffer, 4);
/* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */
if (priv->reg[2] & priv->reg[1] & 0x10)
parport_generic_irq(pp);
return 1;
}
#endif
static void parport_uss720_write_data(struct parport *pp, unsigned char d)
{
set_1284_register(pp, 0, d, GFP_KERNEL);
}
static unsigned char parport_uss720_read_data(struct parport *pp)
{
unsigned char ret;
if (get_1284_register(pp, 0, &ret, GFP_KERNEL))
return 0;
return ret;
}
static void parport_uss720_write_control(struct parport *pp, unsigned char d)
{
struct parport_uss720_private *priv = pp->private_data;
d = (d & 0xf) | (priv->reg[1] & 0xf0);
if (set_1284_register(pp, 2, d, GFP_KERNEL))
return;
priv->reg[1] = d;
}
static unsigned char parport_uss720_read_control(struct parport *pp)
{
struct parport_uss720_private *priv = pp->private_data;
return priv->reg[1] & 0xf; /* Use soft copy */
}
static unsigned char parport_uss720_frob_control(struct parport *pp, unsigned char mask, unsigned char val)
{
struct parport_uss720_private *priv = pp->private_data;
unsigned char d;
mask &= 0x0f;
val &= 0x0f;
d = (priv->reg[1] & (~mask)) ^ val;
if (set_1284_register(pp, 2, d, GFP_KERNEL))
return 0;
priv->reg[1] = d;
return d & 0xf;
}
static unsigned char parport_uss720_read_status(struct parport *pp)
{
unsigned char ret;
if (get_1284_register(pp, 1, &ret, GFP_KERNEL))
return 0;
return ret & 0xf8;
}
static void parport_uss720_disable_irq(struct parport *pp)
{
struct parport_uss720_private *priv = pp->private_data;
unsigned char d;
d = priv->reg[1] & ~0x10;
if (set_1284_register(pp, 2, d, GFP_KERNEL))
return;
priv->reg[1] = d;
}
static void parport_uss720_enable_irq(struct parport *pp)
{
struct parport_uss720_private *priv = pp->private_data;
unsigned char d;
d = priv->reg[1] | 0x10;
if (set_1284_register(pp, 2, d, GFP_KERNEL))
return;
priv->reg[1] = d;
}
static void parport_uss720_data_forward (struct parport *pp)
{
struct parport_uss720_private *priv = pp->private_data;
unsigned char d;
d = priv->reg[1] & ~0x20;
if (set_1284_register(pp, 2, d, GFP_KERNEL))
return;
priv->reg[1] = d;
}
static void parport_uss720_data_reverse (struct parport *pp)
{
struct parport_uss720_private *priv = pp->private_data;
unsigned char d;
d = priv->reg[1] | 0x20;
if (set_1284_register(pp, 2, d, GFP_KERNEL))
return;
priv->reg[1] = d;
}
static void parport_uss720_init_state(struct pardevice *dev, struct parport_state *s)
{
s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
s->u.pc.ecr = 0x24;
}
static void parport_uss720_save_state(struct parport *pp, struct parport_state *s)
{
struct parport_uss720_private *priv = pp->private_data;
#if 0
if (get_1284_register(pp, 2, NULL, GFP_ATOMIC))
return;
#endif
s->u.pc.ctr = priv->reg[1];
s->u.pc.ecr = priv->reg[2];
}
static void parport_uss720_restore_state(struct parport *pp, struct parport_state *s)
{
struct parport_uss720_private *priv = pp->private_data;
set_1284_register(pp, 2, s->u.pc.ctr, GFP_ATOMIC);
set_1284_register(pp, 6, s->u.pc.ecr, GFP_ATOMIC);
get_1284_register(pp, 2, NULL, GFP_ATOMIC);
priv->reg[1] = s->u.pc.ctr;
priv->reg[2] = s->u.pc.ecr;
}
static size_t parport_uss720_epp_read_data(struct parport *pp, void *buf, size_t length, int flags)
{
struct parport_uss720_private *priv = pp->private_data;
size_t got = 0;
if (change_mode(pp, ECR_EPP))
return 0;
for (; got < length; got++) {
if (get_1284_register(pp, 4, (char *)buf, GFP_KERNEL))
break;
buf++;
if (priv->reg[0] & 0x01) {
clear_epp_timeout(pp);
break;
}
}
change_mode(pp, ECR_PS2);
return got;
}
static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf, size_t length, int flags)
{
#if 0
struct parport_uss720_private *priv = pp->private_data;
size_t written = 0;
if (change_mode(pp, ECR_EPP))
return 0;
for (; written < length; written++) {
if (set_1284_register(pp, 4, (char *)buf, GFP_KERNEL))
break;
((char*)buf)++;
if (get_1284_register(pp, 1, NULL, GFP_KERNEL))
break;
if (priv->reg[0] & 0x01) {
clear_epp_timeout(pp);
break;
}
}
change_mode(pp, ECR_PS2);
return written;
#else
struct parport_uss720_private *priv = pp->private_data;
struct usb_device *usbdev = priv->usbdev;
int rlen;
int i;
if (!usbdev)
return 0;
if (change_mode(pp, ECR_EPP))
return 0;
i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buf, length, &rlen, 20000);
if (i)
printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buf, length, rlen);
change_mode(pp, ECR_PS2);
return rlen;
#endif
}
static size_t parport_uss720_epp_read_addr(struct parport *pp, void *buf, size_t length, int flags)
{
struct parport_uss720_private *priv = pp->private_data;
size_t got = 0;
if (change_mode(pp, ECR_EPP))
return 0;
for (; got < length; got++) {
if (get_1284_register(pp, 3, (char *)buf, GFP_KERNEL))
break;
buf++;
if (priv->reg[0] & 0x01) {
clear_epp_timeout(pp);
break;
}
}
change_mode(pp, ECR_PS2);
return got;
}
static size_t parport_uss720_epp_write_addr(struct parport *pp, const void *buf, size_t length, int flags)
{
struct parport_uss720_private *priv = pp->private_data;
size_t written = 0;
if (change_mode(pp, ECR_EPP))
return 0;
for (; written < length; written++) {
if (set_1284_register(pp, 3, *(char *)buf, GFP_KERNEL))
break;
buf++;
if (get_1284_register(pp, 1, NULL, GFP_KERNEL))
break;
if (priv->reg[0] & 0x01) {
clear_epp_timeout(pp);
break;
}
}
change_mode(pp, ECR_PS2);
return written;
}
static size_t parport_uss720_ecp_write_data(struct parport *pp, const void *buffer, size_t len, int flags)
{
struct parport_uss720_private *priv = pp->private_data;
struct usb_device *usbdev = priv->usbdev;
int rlen;
int i;
if (!usbdev)
return 0;
if (change_mode(pp, ECR_ECP))
return 0;
i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, 20000);
if (i)
printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buffer, len, rlen);
change_mode(pp, ECR_PS2);
return rlen;
}
static size_t parport_uss720_ecp_read_data(struct parport *pp, void *buffer, size_t len, int flags)
{
struct parport_uss720_private *priv = pp->private_data;
struct usb_device *usbdev = priv->usbdev;
int rlen;
int i;
if (!usbdev)
return 0;
if (change_mode(pp, ECR_ECP))
return 0;
i = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, 20000);
if (i)
printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %Zu rlen %u\n", buffer, len, rlen);
change_mode(pp, ECR_PS2);
return rlen;
}
static size_t parport_uss720_ecp_write_addr(struct parport *pp, const void *buffer, size_t len, int flags)
{
size_t written = 0;
if (change_mode(pp, ECR_ECP))
return 0;
for (; written < len; written++) {
if (set_1284_register(pp, 5, *(char *)buffer, GFP_KERNEL))
break;
buffer++;
}
change_mode(pp, ECR_PS2);
return written;
}
static size_t parport_uss720_write_compat(struct parport *pp, const void *buffer, size_t len, int flags)
{
struct parport_uss720_private *priv = pp->private_data;
struct usb_device *usbdev = priv->usbdev;
int rlen;
int i;
if (!usbdev)
return 0;
if (change_mode(pp, ECR_PPF))
return 0;
i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, 20000);
if (i)
printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buffer, len, rlen);
change_mode(pp, ECR_PS2);
return rlen;
}
/* --------------------------------------------------------------------- */
static struct parport_operations parport_uss720_ops =
{
.owner = THIS_MODULE,
.write_data = parport_uss720_write_data,
.read_data = parport_uss720_read_data,
.write_control = parport_uss720_write_control,
.read_control = parport_uss720_read_control,
.frob_control = parport_uss720_frob_control,
.read_status = parport_uss720_read_status,
.enable_irq = parport_uss720_enable_irq,
.disable_irq = parport_uss720_disable_irq,
.data_forward = parport_uss720_data_forward,
.data_reverse = parport_uss720_data_reverse,
.init_state = parport_uss720_init_state,
.save_state = parport_uss720_save_state,
.restore_state = parport_uss720_restore_state,
.epp_write_data = parport_uss720_epp_write_data,
.epp_read_data = parport_uss720_epp_read_data,
.epp_write_addr = parport_uss720_epp_write_addr,
.epp_read_addr = parport_uss720_epp_read_addr,
.ecp_write_data = parport_uss720_ecp_write_data,
.ecp_read_data = parport_uss720_ecp_read_data,
.ecp_write_addr = parport_uss720_ecp_write_addr,
.compat_write_data = parport_uss720_write_compat,
.nibble_read_data = parport_ieee1284_read_nibble,
.byte_read_data = parport_ieee1284_read_byte,
};
/* --------------------------------------------------------------------- */
static int uss720_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *usbdev = usb_get_dev(interface_to_usbdev(intf));
struct usb_host_interface *interface;
struct usb_host_endpoint *endpoint;
struct parport_uss720_private *priv;
struct parport *pp;
unsigned char reg;
int i;
dbg("probe: vendor id 0x%x, device id 0x%x\n",
le16_to_cpu(usbdev->descriptor.idVendor),
le16_to_cpu(usbdev->descriptor.idProduct));
/* our known interfaces have 3 alternate settings */
if (intf->num_altsetting != 3) {
usb_put_dev(usbdev);
return -ENODEV;
}
i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
dbg("set inteface result %d", i);
interface = intf->cur_altsetting;
/*
* Allocate parport interface
*/
if (!(priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL))) {
usb_put_dev(usbdev);
return -ENOMEM;
}
priv->pp = NULL;
priv->usbdev = usbdev;
kref_init(&priv->ref_count);
spin_lock_init(&priv->asynclock);
INIT_LIST_HEAD(&priv->asynclist);
if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) {
printk(KERN_WARNING "uss720: could not register parport\n");
goto probe_abort;
}
priv->pp = pp;
pp->private_data = priv;
pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
/* set the USS720 control register to manual mode, no ECP compression, enable all ints */
set_1284_register(pp, 7, 0x00, GFP_KERNEL);
set_1284_register(pp, 6, 0x30, GFP_KERNEL); /* PS/2 mode */
set_1284_register(pp, 2, 0x0c, GFP_KERNEL);
/* debugging */
get_1284_register(pp, 0, &reg, GFP_KERNEL);
dbg("reg: %02x %02x %02x %02x %02x %02x %02x",
priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]);
endpoint = &interface->endpoint[2];
dbg("epaddr %d interval %d", endpoint->desc.bEndpointAddress, endpoint->desc.bInterval);
parport_announce_port(pp);
usb_set_intfdata(intf, pp);
return 0;
probe_abort:
kill_all_async_requests_priv(priv);
kref_put(&priv->ref_count, destroy_priv);
return -ENODEV;
}
static void uss720_disconnect(struct usb_interface *intf)
{
struct parport *pp = usb_get_intfdata(intf);
struct parport_uss720_private *priv;
struct usb_device *usbdev;
dbg("disconnect");
usb_set_intfdata(intf, NULL);
if (pp) {
priv = pp->private_data;
usbdev = priv->usbdev;
priv->usbdev = NULL;
priv->pp = NULL;
dbg("parport_remove_port");
parport_remove_port(pp);
parport_put_port(pp);
kill_all_async_requests_priv(priv);
kref_put(&priv->ref_count, destroy_priv);
}
dbg("disconnect done");
}
/* table of cables that work through this driver */
static struct usb_device_id uss720_table [] = {
{ USB_DEVICE(0x047e, 0x1001) },
{ USB_DEVICE(0x0557, 0x2001) },
{ USB_DEVICE(0x0729, 0x1284) },
{ USB_DEVICE(0x1293, 0x0002) },
{ USB_DEVICE(0x1293, 0x0002) },
{ USB_DEVICE(0x050d, 0x0002) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, uss720_table);
static struct usb_driver uss720_driver = {
.name = "uss720",
.probe = uss720_probe,
.disconnect = uss720_disconnect,
.id_table = uss720_table,
};
/* --------------------------------------------------------------------- */
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static int __init uss720_init(void)
{
int retval;
retval = usb_register(&uss720_driver);
if (retval)
goto out;
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
printk(KERN_INFO KBUILD_MODNAME ": NOTE: this is a special purpose "
"driver to allow nonstandard\n");
printk(KERN_INFO KBUILD_MODNAME ": protocols (eg. bitbang) over "
"USS720 usb to parallel cables\n");
printk(KERN_INFO KBUILD_MODNAME ": If you just want to connect to a "
"printer, use usblp instead\n");
out:
return retval;
}
static void __exit uss720_cleanup(void)
{
usb_deregister(&uss720_driver);
}
module_init(uss720_init);
module_exit(uss720_cleanup);
/* --------------------------------------------------------------------- */

View File

@@ -0,0 +1,783 @@
/*****************************************************************************
* File: drivers/usb/misc/vstusb.c
*
* Purpose: Support for the bulk USB Vernier Spectrophotometers
*
* Author: Johnnie Peters
* Axian Consulting
* Beaverton, OR, USA 97005
*
* Modified by: EQware Engineering, Inc.
* Oregon City, OR, USA 97045
*
* Copyright: 2007, 2008
* Vernier Software & Technology
* Beaverton, OR, USA 97005
*
* Web: www.vernier.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*****************************************************************************/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/vstusb.h>
#define DRIVER_VERSION "VST USB Driver Version 1.5"
#define DRIVER_DESC "Vernier Software Technology Bulk USB Driver"
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define VSTUSB_MINOR_BASE 0
#else
#define VSTUSB_MINOR_BASE 199
#endif
#define USB_VENDOR_OCEANOPTICS 0x2457
#define USB_VENDOR_VERNIER 0x08F7 /* Vernier Software & Technology */
#define USB_PRODUCT_USB2000 0x1002
#define USB_PRODUCT_ADC1000_FW 0x1003 /* firmware download (renumerates) */
#define USB_PRODUCT_ADC1000 0x1004
#define USB_PRODUCT_HR2000_FW 0x1009 /* firmware download (renumerates) */
#define USB_PRODUCT_HR2000 0x100A
#define USB_PRODUCT_HR4000_FW 0x1011 /* firmware download (renumerates) */
#define USB_PRODUCT_HR4000 0x1012
#define USB_PRODUCT_USB650 0x1014 /* "Red Tide" */
#define USB_PRODUCT_QE65000 0x1018
#define USB_PRODUCT_USB4000 0x1022
#define USB_PRODUCT_USB325 0x1024 /* "Vernier Spectrometer" */
#define USB_PRODUCT_LABPRO 0x0001
#define USB_PRODUCT_LABQUEST 0x0005
#define VST_MAXBUFFER (64*1024)
static struct usb_device_id id_table[] = {
{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB2000)},
{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_HR4000)},
{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB650)},
{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB4000)},
{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB325)},
{ USB_DEVICE(USB_VENDOR_VERNIER, USB_PRODUCT_LABQUEST)},
{ USB_DEVICE(USB_VENDOR_VERNIER, USB_PRODUCT_LABPRO)},
{},
};
MODULE_DEVICE_TABLE(usb, id_table);
struct vstusb_device {
struct kref kref;
struct mutex lock;
struct usb_device *usb_dev;
char present;
char isopen;
struct usb_anchor submitted;
int rd_pipe;
int rd_timeout_ms;
int wr_pipe;
int wr_timeout_ms;
};
#define to_vst_dev(d) container_of(d, struct vstusb_device, kref)
static struct usb_driver vstusb_driver;
static void vstusb_delete(struct kref *kref)
{
struct vstusb_device *vstdev = to_vst_dev(kref);
usb_put_dev(vstdev->usb_dev);
kfree(vstdev);
}
static int vstusb_open(struct inode *inode, struct file *file)
{
struct vstusb_device *vstdev;
struct usb_interface *interface;
interface = usb_find_interface(&vstusb_driver, iminor(inode));
if (!interface) {
printk(KERN_ERR KBUILD_MODNAME
": %s - error, can't find device for minor %d\n",
__func__, iminor(inode));
return -ENODEV;
}
vstdev = usb_get_intfdata(interface);
if (!vstdev)
return -ENODEV;
/* lock this device */
mutex_lock(&vstdev->lock);
/* can only open one time */
if ((!vstdev->present) || (vstdev->isopen)) {
mutex_unlock(&vstdev->lock);
return -EBUSY;
}
/* increment our usage count */
kref_get(&vstdev->kref);
vstdev->isopen = 1;
/* save device in the file's private structure */
file->private_data = vstdev;
dev_dbg(&vstdev->usb_dev->dev, "%s: opened\n", __func__);
mutex_unlock(&vstdev->lock);
return 0;
}
static int vstusb_release(struct inode *inode, struct file *file)
{
struct vstusb_device *vstdev;
vstdev = file->private_data;
if (vstdev == NULL)
return -ENODEV;
mutex_lock(&vstdev->lock);
vstdev->isopen = 0;
dev_dbg(&vstdev->usb_dev->dev, "%s: released\n", __func__);
mutex_unlock(&vstdev->lock);
kref_put(&vstdev->kref, vstusb_delete);
return 0;
}
static void usb_api_blocking_completion(struct urb *urb)
{
struct completion *completeit = urb->context;
complete(completeit);
}
static int vstusb_fill_and_send_urb(struct urb *urb,
struct usb_device *usb_dev,
unsigned int pipe, void *data,
unsigned int len, struct completion *done)
{
struct usb_host_endpoint *ep;
struct usb_host_endpoint **hostep;
unsigned int pipend;
int status;
hostep = usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out;
pipend = usb_pipeendpoint(pipe);
ep = hostep[pipend];
if (!ep || (len == 0))
return -EINVAL;
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_INT) {
pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
usb_fill_int_urb(urb, usb_dev, pipe, data, len,
(usb_complete_t)usb_api_blocking_completion,
NULL, ep->desc.bInterval);
} else
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
(usb_complete_t)usb_api_blocking_completion,
NULL);
init_completion(done);
urb->context = done;
urb->actual_length = 0;
status = usb_submit_urb(urb, GFP_KERNEL);
return status;
}
static int vstusb_complete_urb(struct urb *urb, struct completion *done,
int timeout, int *actual_length)
{
unsigned long expire;
int status;
expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
if (!wait_for_completion_interruptible_timeout(done, expire)) {
usb_kill_urb(urb);
status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status;
dev_dbg(&urb->dev->dev,
"%s timed out on ep%d%s len=%d/%d, urb status = %d\n",
current->comm,
usb_pipeendpoint(urb->pipe),
usb_pipein(urb->pipe) ? "in" : "out",
urb->actual_length,
urb->transfer_buffer_length,
urb->status);
} else {
if (signal_pending(current)) {
/* if really an error */
if (urb->status && !((urb->status == -ENOENT) ||
(urb->status == -ECONNRESET) ||
(urb->status == -ESHUTDOWN))) {
status = -EINTR;
usb_kill_urb(urb);
} else {
status = 0;
}
dev_dbg(&urb->dev->dev,
"%s: signal pending on ep%d%s len=%d/%d,"
"urb status = %d\n",
current->comm,
usb_pipeendpoint(urb->pipe),
usb_pipein(urb->pipe) ? "in" : "out",
urb->actual_length,
urb->transfer_buffer_length,
urb->status);
} else {
status = urb->status;
}
}
if (actual_length)
*actual_length = urb->actual_length;
return status;
}
static ssize_t vstusb_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct vstusb_device *vstdev;
int cnt = -1;
void *buf;
int retval = 0;
struct urb *urb;
struct usb_device *dev;
unsigned int pipe;
int timeout;
DECLARE_COMPLETION_ONSTACK(done);
vstdev = file->private_data;
if (vstdev == NULL)
return -ENODEV;
/* verify that we actually want to read some data */
if ((count == 0) || (count > VST_MAXBUFFER))
return -EINVAL;
/* lock this object */
if (mutex_lock_interruptible(&vstdev->lock))
return -ERESTARTSYS;
/* anyone home */
if (!vstdev->present) {
mutex_unlock(&vstdev->lock);
printk(KERN_ERR KBUILD_MODNAME
": %s: device not present\n", __func__);
return -ENODEV;
}
/* pull out the necessary data */
dev = vstdev->usb_dev;
pipe = usb_rcvbulkpipe(dev, vstdev->rd_pipe);
timeout = vstdev->rd_timeout_ms;
buf = kmalloc(count, GFP_KERNEL);
if (buf == NULL) {
mutex_unlock(&vstdev->lock);
return -ENOMEM;
}
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
kfree(buf);
mutex_unlock(&vstdev->lock);
return -ENOMEM;
}
usb_anchor_urb(urb, &vstdev->submitted);
retval = vstusb_fill_and_send_urb(urb, dev, pipe, buf, count, &done);
mutex_unlock(&vstdev->lock);
if (retval) {
usb_unanchor_urb(urb);
dev_err(&dev->dev, "%s: error %d filling and sending urb %d\n",
__func__, retval, pipe);
goto exit;
}
retval = vstusb_complete_urb(urb, &done, timeout, &cnt);
if (retval) {
dev_err(&dev->dev, "%s: error %d completing urb %d\n",
__func__, retval, pipe);
goto exit;
}
if (copy_to_user(buffer, buf, cnt)) {
dev_err(&dev->dev, "%s: can't copy_to_user\n", __func__);
retval = -EFAULT;
} else {
retval = cnt;
dev_dbg(&dev->dev, "%s: read %d bytes from pipe %d\n",
__func__, cnt, pipe);
}
exit:
usb_free_urb(urb);
kfree(buf);
return retval;
}
static ssize_t vstusb_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct vstusb_device *vstdev;
int cnt = -1;
void *buf;
int retval = 0;
struct urb *urb;
struct usb_device *dev;
unsigned int pipe;
int timeout;
DECLARE_COMPLETION_ONSTACK(done);
vstdev = file->private_data;
if (vstdev == NULL)
return -ENODEV;
/* verify that we actually have some data to write */
if ((count == 0) || (count > VST_MAXBUFFER))
return retval;
/* lock this object */
if (mutex_lock_interruptible(&vstdev->lock))
return -ERESTARTSYS;
/* anyone home */
if (!vstdev->present) {
mutex_unlock(&vstdev->lock);
printk(KERN_ERR KBUILD_MODNAME
": %s: device not present\n", __func__);
return -ENODEV;
}
/* pull out the necessary data */
dev = vstdev->usb_dev;
pipe = usb_sndbulkpipe(dev, vstdev->wr_pipe);
timeout = vstdev->wr_timeout_ms;
buf = kmalloc(count, GFP_KERNEL);
if (buf == NULL) {
mutex_unlock(&vstdev->lock);
return -ENOMEM;
}
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
kfree(buf);
mutex_unlock(&vstdev->lock);
return -ENOMEM;
}
if (copy_from_user(buf, buffer, count)) {
mutex_unlock(&vstdev->lock);
dev_err(&dev->dev, "%s: can't copy_from_user\n", __func__);
retval = -EFAULT;
goto exit;
}
usb_anchor_urb(urb, &vstdev->submitted);
retval = vstusb_fill_and_send_urb(urb, dev, pipe, buf, count, &done);
mutex_unlock(&vstdev->lock);
if (retval) {
usb_unanchor_urb(urb);
dev_err(&dev->dev, "%s: error %d filling and sending urb %d\n",
__func__, retval, pipe);
goto exit;
}
retval = vstusb_complete_urb(urb, &done, timeout, &cnt);
if (retval) {
dev_err(&dev->dev, "%s: error %d completing urb %d\n",
__func__, retval, pipe);
goto exit;
} else {
retval = cnt;
dev_dbg(&dev->dev, "%s: sent %d bytes to pipe %d\n",
__func__, cnt, pipe);
}
exit:
usb_free_urb(urb);
kfree(buf);
return retval;
}
static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int retval = 0;
int cnt = -1;
void __user *data = (void __user *)arg;
struct vstusb_args usb_data;
struct vstusb_device *vstdev;
void *buffer = NULL; /* must be initialized. buffer is
* referenced on exit but not all
* ioctls allocate it */
struct urb *urb = NULL; /* must be initialized. urb is
* referenced on exit but not all
* ioctls allocate it */
struct usb_device *dev;
unsigned int pipe;
int timeout;
DECLARE_COMPLETION_ONSTACK(done);
vstdev = file->private_data;
if (_IOC_TYPE(cmd) != VST_IOC_MAGIC) {
dev_warn(&vstdev->usb_dev->dev,
"%s: ioctl command %x, bad ioctl magic %x, "
"expected %x\n", __func__, cmd,
_IOC_TYPE(cmd), VST_IOC_MAGIC);
return -EINVAL;
}
if (vstdev == NULL)
return -ENODEV;
if (copy_from_user(&usb_data, data, sizeof(struct vstusb_args))) {
dev_err(&vstdev->usb_dev->dev, "%s: can't copy_from_user\n",
__func__);
return -EFAULT;
}
/* lock this object */
if (mutex_lock_interruptible(&vstdev->lock)) {
retval = -ERESTARTSYS;
goto exit;
}
/* anyone home */
if (!vstdev->present) {
mutex_unlock(&vstdev->lock);
dev_err(&vstdev->usb_dev->dev, "%s: device not present\n",
__func__);
retval = -ENODEV;
goto exit;
}
/* pull out the necessary data */
dev = vstdev->usb_dev;
switch (cmd) {
case IOCTL_VSTUSB_CONFIG_RW:
vstdev->rd_pipe = usb_data.rd_pipe;
vstdev->rd_timeout_ms = usb_data.rd_timeout_ms;
vstdev->wr_pipe = usb_data.wr_pipe;
vstdev->wr_timeout_ms = usb_data.wr_timeout_ms;
mutex_unlock(&vstdev->lock);
dev_dbg(&dev->dev, "%s: setting pipes/timeouts, "
"rdpipe = %d, rdtimeout = %d, "
"wrpipe = %d, wrtimeout = %d\n", __func__,
vstdev->rd_pipe, vstdev->rd_timeout_ms,
vstdev->wr_pipe, vstdev->wr_timeout_ms);
break;
case IOCTL_VSTUSB_SEND_PIPE:
if ((usb_data.count == 0) || (usb_data.count > VST_MAXBUFFER)) {
mutex_unlock(&vstdev->lock);
retval = -EINVAL;
goto exit;
}
buffer = kmalloc(usb_data.count, GFP_KERNEL);
if (buffer == NULL) {
mutex_unlock(&vstdev->lock);
retval = -ENOMEM;
goto exit;
}
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
mutex_unlock(&vstdev->lock);
retval = -ENOMEM;
goto exit;
}
timeout = usb_data.timeout_ms;
pipe = usb_sndbulkpipe(dev, usb_data.pipe);
if (copy_from_user(buffer, usb_data.buffer, usb_data.count)) {
dev_err(&dev->dev, "%s: can't copy_from_user\n",
__func__);
mutex_unlock(&vstdev->lock);
retval = -EFAULT;
goto exit;
}
usb_anchor_urb(urb, &vstdev->submitted);
retval = vstusb_fill_and_send_urb(urb, dev, pipe, buffer,
usb_data.count, &done);
mutex_unlock(&vstdev->lock);
if (retval) {
usb_unanchor_urb(urb);
dev_err(&dev->dev,
"%s: error %d filling and sending urb %d\n",
__func__, retval, pipe);
goto exit;
}
retval = vstusb_complete_urb(urb, &done, timeout, &cnt);
if (retval) {
dev_err(&dev->dev, "%s: error %d completing urb %d\n",
__func__, retval, pipe);
}
break;
case IOCTL_VSTUSB_RECV_PIPE:
if ((usb_data.count == 0) || (usb_data.count > VST_MAXBUFFER)) {
mutex_unlock(&vstdev->lock);
retval = -EINVAL;
goto exit;
}
buffer = kmalloc(usb_data.count, GFP_KERNEL);
if (buffer == NULL) {
mutex_unlock(&vstdev->lock);
retval = -ENOMEM;
goto exit;
}
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
mutex_unlock(&vstdev->lock);
retval = -ENOMEM;
goto exit;
}
timeout = usb_data.timeout_ms;
pipe = usb_rcvbulkpipe(dev, usb_data.pipe);
usb_anchor_urb(urb, &vstdev->submitted);
retval = vstusb_fill_and_send_urb(urb, dev, pipe, buffer,
usb_data.count, &done);
mutex_unlock(&vstdev->lock);
if (retval) {
usb_unanchor_urb(urb);
dev_err(&dev->dev,
"%s: error %d filling and sending urb %d\n",
__func__, retval, pipe);
goto exit;
}
retval = vstusb_complete_urb(urb, &done, timeout, &cnt);
if (retval) {
dev_err(&dev->dev, "%s: error %d completing urb %d\n",
__func__, retval, pipe);
goto exit;
}
if (copy_to_user(usb_data.buffer, buffer, cnt)) {
dev_err(&dev->dev, "%s: can't copy_to_user\n",
__func__);
retval = -EFAULT;
goto exit;
}
usb_data.count = cnt;
if (copy_to_user(data, &usb_data, sizeof(struct vstusb_args))) {
dev_err(&dev->dev, "%s: can't copy_to_user\n",
__func__);
retval = -EFAULT;
} else {
dev_dbg(&dev->dev, "%s: recv %zd bytes from pipe %d\n",
__func__, usb_data.count, usb_data.pipe);
}
break;
default:
mutex_unlock(&vstdev->lock);
dev_warn(&dev->dev, "ioctl_vstusb: invalid ioctl cmd %x\n",
cmd);
return -EINVAL;
break;
}
exit:
usb_free_urb(urb);
kfree(buffer);
return retval;
}
static const struct file_operations vstusb_fops = {
.owner = THIS_MODULE,
.read = vstusb_read,
.write = vstusb_write,
.unlocked_ioctl = vstusb_ioctl,
.compat_ioctl = vstusb_ioctl,
.open = vstusb_open,
.release = vstusb_release,
};
static struct usb_class_driver usb_vstusb_class = {
.name = "usb/vstusb%d",
.fops = &vstusb_fops,
.minor_base = VSTUSB_MINOR_BASE,
};
static int vstusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct vstusb_device *vstdev;
int i;
int retval = 0;
/* allocate memory for our device state and intialize it */
vstdev = kzalloc(sizeof(*vstdev), GFP_KERNEL);
if (vstdev == NULL)
return -ENOMEM;
/* must do usb_get_dev() prior to kref_init() since the kref_put()
* release function will do a usb_put_dev() */
usb_get_dev(dev);
kref_init(&vstdev->kref);
mutex_init(&vstdev->lock);
i = dev->descriptor.bcdDevice;
dev_dbg(&intf->dev, "Version %1d%1d.%1d%1d found at address %d\n",
(i & 0xF000) >> 12, (i & 0xF00) >> 8,
(i & 0xF0) >> 4, (i & 0xF), dev->devnum);
vstdev->present = 1;
vstdev->isopen = 0;
vstdev->usb_dev = dev;
init_usb_anchor(&vstdev->submitted);
usb_set_intfdata(intf, vstdev);
retval = usb_register_dev(intf, &usb_vstusb_class);
if (retval) {
dev_err(&intf->dev,
"%s: Not able to get a minor for this device.\n",
__func__);
usb_set_intfdata(intf, NULL);
kref_put(&vstdev->kref, vstusb_delete);
return retval;
}
/* let the user know what node this device is now attached to */
dev_info(&intf->dev,
"VST USB Device #%d now attached to major %d minor %d\n",
(intf->minor - VSTUSB_MINOR_BASE), USB_MAJOR, intf->minor);
dev_info(&intf->dev, "%s, %s\n", DRIVER_DESC, DRIVER_VERSION);
return retval;
}
static void vstusb_disconnect(struct usb_interface *intf)
{
struct vstusb_device *vstdev = usb_get_intfdata(intf);
usb_deregister_dev(intf, &usb_vstusb_class);
usb_set_intfdata(intf, NULL);
if (vstdev) {
mutex_lock(&vstdev->lock);
vstdev->present = 0;
usb_kill_anchored_urbs(&vstdev->submitted);
mutex_unlock(&vstdev->lock);
kref_put(&vstdev->kref, vstusb_delete);
}
}
static int vstusb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct vstusb_device *vstdev = usb_get_intfdata(intf);
int time;
if (!vstdev)
return 0;
mutex_lock(&vstdev->lock);
time = usb_wait_anchor_empty_timeout(&vstdev->submitted, 1000);
if (!time)
usb_kill_anchored_urbs(&vstdev->submitted);
mutex_unlock(&vstdev->lock);
return 0;
}
static int vstusb_resume(struct usb_interface *intf)
{
return 0;
}
static struct usb_driver vstusb_driver = {
.name = "vstusb",
.probe = vstusb_probe,
.disconnect = vstusb_disconnect,
.suspend = vstusb_suspend,
.resume = vstusb_resume,
.id_table = id_table,
};
static int __init vstusb_init(void)
{
int rc;
rc = usb_register(&vstusb_driver);
if (rc)
printk(KERN_ERR "%s: failed to register (%d)", __func__, rc);
return rc;
}
static void __exit vstusb_exit(void)
{
usb_deregister(&vstusb_driver);
}
module_init(vstusb_init);
module_exit(vstusb_exit);
MODULE_AUTHOR("Dennis O'Brien/Stephen Ware");
MODULE_DESCRIPTION(DRIVER_VERSION);
MODULE_LICENSE("GPL");