synaptics-usb 1.5rc8 for hacking around with the touchpad (no mods made for detection yet)

This commit is contained in:
Matt Sealey
2011-01-24 11:56:07 -06:00
parent 5e231689ba
commit 6f61305258
8 changed files with 1942 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.31.14.13
# Mon Jan 17 14:25:05 2011
# Linux kernel version: 2.6.31.14.17
# Mon Jan 24 11:47:01 2011
#
CONFIG_ARM=y
CONFIG_HAVE_PWM=y
@@ -1179,6 +1179,8 @@ CONFIG_INPUT_MOUSE=y
# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_MOUSE_GPIO is not set
# CONFIG_MOUSE_SYNAPTICS_I2C is not set
CONFIG_MOUSE_SYNAPTICS_USB=m
# CONFIG_MOUSE_SYNAPTICS_CPADDEV is not set
CONFIG_INPUT_JOYSTICK=y
# CONFIG_JOYSTICK_ANALOG is not set
# CONFIG_JOYSTICK_A3D is not set
@@ -1401,6 +1403,7 @@ CONFIG_POWER_SUPPLY=y
# CONFIG_BATTERY_DS2782 is not set
# CONFIG_BATTERY_BQ27x00 is not set
# CONFIG_BATTERY_MAX17040 is not set
# CONFIG_BATTERY_SBS is not set
CONFIG_BATTERY_EFIKASB=y
# CONFIG_HWMON is not set
# CONFIG_THERMAL is not set

View File

@@ -321,4 +321,28 @@ config MOUSE_SYNAPTICS_I2C
To compile this driver as a module, choose M here: the
module will be called synaptics_i2c.
config MOUSE_SYNAPTICS_USB
tristate "Synaptics USB Touchpad support"
depends on USB
help
Say Y here if you want to use a Synaptics USB touchpad. Synaptics USB
touchpads emulate an USB mouse by default, so you can also use the
usbhid driver. But you will be able to use Synaptics TouchPad driver
for X.Org in combination with this kernel module, which will improve
mouse movement, enable vertical/horizontal scrolling, etc. More
information can be found at:
<http://jan-steinhoff.de/linux/synaptics-usb.html>
To compile this driver as a module, choose M here: the
module will be called synaptics_usb.
config MOUSE_SYNAPTICS_CPADDEV
bool "Synaptics cPad display support"
depends on MOUSE_SYNAPTICS_USB
help
Say Y here if you want to use the display of Synaptics cPad. The
display can be acessed through a character device /dev/usb/cpad?.
Programs that use the character device can be found at:
<http://jan-steinhoff.de/linux/synaptics-usb.html>
endif

View File

@@ -19,6 +19,7 @@ obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
psmouse-objs := psmouse-base.o synaptics.o
@@ -30,3 +31,7 @@ psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
synaptics_usb-objs := synapticsusb.o
synaptics_usb-$(CONFIG_MOUSE_SYNAPTICS_CPADDEV) += cpad.o

950
drivers/input/mouse/cpad.c Normal file
View File

@@ -0,0 +1,950 @@
/****
* cPad character device part of the synaptics_usb driver
*
* Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk)
* Copyright (c) 2003 Ron Lee (ron@debian.org)
* cPad driver for kernel 2.4
*
* Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de)
* Copyright (c) 2004 Ron Lee (ron@debian.org)
* rewritten for kernel 2.6
*
* Driver core and input device part are in synapticsusb.c
*
* Bases on: usb_skeleton.c v2.2 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; either version 2 of the License, or (at your option)
* any later version.
*
* Trademarks are the property of their respective owners.
*/
/****
* The cPad is an USB touchpad with background display (240x160 mono).
* It has one interface with three possible alternate settings:
* setting 0: one int endpoint for relative movement (used by usbhid.ko)
* setting 1: one int endpoint for absolute finger position
* setting 2: one int endpoint for absolute finger position and
* two bulk endpoints for the display (in/out)
* This driver switches to setting 2 in synapticsusb.c if the device has a
* display.
*
* How the bulk endpoints work:
*
* The cPad display is controlled by a Seiko/Epson 1335 LCD Controller IC.
* In order to send commands to the sed1335, each packet in the urb must look
* like this:
* 02 <1335 command> [<data in reversed order> ...]
* Possible commands for the sed1335 are listed in linux/cpad.h. The data must
* be in reversed order as stated in the sed1335 data sheet.
* The urb must be send to the bulk out endpoint. Because the packet size of
* this endoint is 32 bytes, "02 <1335 command>" must be repeated after 30
* bytes of data. The data must be in reversed order in each of these 30 bytes
* blocks. All this is done automatically when writing to the character device.
*
* Functions that are not controlled by the sed1335, like the backlight, can
* be accessed by
* 01 <function> <state>
* The packet must be send to the bulk out endpoint. These functions can be
* accessed via ioctls. Possible functions are listed in cpad.h.
*
* An urb to the bulk out endpoint should be followed by an urb to the bulk in
* endpoint. This gives the answer of the cpad/sed1335, accessible by reading
* from the character device.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/cpad.h>
#include "synapticsusb.h"
#include "cpad.h"
/*
* helper functions
*/
static inline void cpad_unlock(struct syndisplay *display)
{
mutex_unlock(&display->io_mutex);
}
/* lock io_mutex and notify about reset; is interruptible. */
static int cpad_lock(struct syndisplay *display, struct file *file)
{
int retval;
if (!mutex_trylock(&display->io_mutex)) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
if (mutex_lock_interruptible(&display->io_mutex))
return -ERESTARTSYS;
}
if (display->gone) {
retval = -ENODEV;
goto error;
}
if (display->suspended) {
retval = -EHOSTUNREACH;
goto error;
}
if (display->reset_in_progress || display->reset_notify) {
display->reset_notify = 0;
retval = -EPIPE;
goto error;
}
return 0;
error:
cpad_unlock(display);
return retval;
}
static void cpad_in_callback(struct urb *urb)
{
struct cpad_urb *curb = (struct cpad_urb *)urb->context;
if (synusb_urb_status_error(urb))
cpad_err(curb->display, "nonzero read bulk status received: %d",
urb->status);
complete(&curb->display->done);
}
static void cpad_out_callback(struct urb *urb)
{
struct cpad_urb *curb = (struct cpad_urb *)urb->context;
struct syndisplay *display = curb->display;
if (urb->status) {
if (synusb_urb_status_error(urb))
cpad_err(display, "nonzero write bulk status "
"received: %d", urb->status);
goto complete;
}
/* submit urb to the bulk in endpoint; reads the answer of the device */
display->error = usb_submit_urb(curb->in, GFP_ATOMIC);
if (!display->error)
return;
cpad_err(display, "usb_submit_urb bulk in failed, error %d",
display->error);
complete:
complete(&display->done);
}
/* Send out and in urbs synchronously. Call with io_mutex held. */
static int cpad_submit(struct cpad_urb *curb)
{
struct syndisplay *display = curb->display;
int retval;
INIT_COMPLETION(display->done);
display->error = 0;
retval = usb_submit_urb(curb->out, GFP_KERNEL);
if (retval) {
cpad_err(display, "usb_submit_urb bulk out failed, error %d",
retval);
goto error;
}
retval = wait_for_completion_interruptible_timeout(&display->done,
msecs_to_jiffies(2000));
if (retval <= 0) {
retval = retval ? retval : -ETIMEDOUT;
usb_kill_urb(curb->out);
usb_kill_urb(curb->in);
goto error;
}
/* report error that occured first */
if (curb->out->status)
retval = curb->out->status;
else if (display->error)
retval = display->error;
else
retval = curb->in->status;
error:
return retval;
}
/* as cpad_submit, but handle reset and has_indata status variable */
static int cpad_submit_user(struct syndisplay *display)
{
int retval;
retval = cpad_submit(display->user_urb);
display->has_indata = retval < 0 ? 0 : 1;
/* to preserve notifications about reset */
if (retval)
retval = (retval == -EPIPE) ? -EIO : retval;
return retval;
}
/* This is a helper function for non-lcd-controller capabilities. For example,
* to turn on the backlight, the arguments func and val must be CPAD_W_LIGHT
* and 1, respectively. The argument which_urb can take the values CPAD_USER_URB
* or CPAD_NLCD_URB, and determines whether display->user_urb or
* display->nlcd_urb is submitted. Call with io_mutex held. */
static int cpad_nlcd_function(struct syndisplay *display,
u8 func, u8 val, int which_urb)
{
struct cpad_urb *curb;
u8 *out_buf;
int retval = 0;
/* we do not allow to write the EEPROM via CPAD_W_ROM */
if ((func <= (u8) CPAD_W_ROM) || (func >= (u8) CPAD_RSRVD)) {
cpad_err(display, "Invalid nlcd command");
return -EINVAL;
}
if (which_urb == CPAD_USER_URB) {
curb = display->user_urb;
curb->out->transfer_buffer_length = 3;
} else
/* nlcd_urb->out->transfer_buffer_length is always 3 */
curb = display->nlcd_urb;
/* fill the transfer buffer */
out_buf = curb->out->transfer_buffer;
*(out_buf++) = SEL_CPAD;
*(out_buf++) = func;
*(out_buf++) = val;
((u8 *)curb->in->transfer_buffer)[2] = 0;
if (which_urb == CPAD_USER_URB)
retval = cpad_submit_user(display);
else
retval = cpad_submit(curb);
if (!retval) {
/* return possible answer of the cpad, if no error occurred */
retval = ((u8 *)curb->in->transfer_buffer)[2];
/* remember backlight state */
if (func == CPAD_W_LIGHT)
display->backlight_on = val ? 1 : 0;
}
return retval;
}
static void cpad_urb_free(struct cpad_urb *curb)
{
if (curb == NULL)
return;
synusb_free_urb(curb->out);
synusb_free_urb(curb->in);
kfree(curb);
}
/* helper for cpad_urb_alloc */
static struct urb *cpad_alloc_bulk(struct cpad_urb *curb, size_t size,
usb_complete_t callback, int pipe)
{
struct usb_device *udev = curb->display->parent->udev;
struct urb *urb;
char *buffer;
/* create an urb, and a buffer for it */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (urb == NULL)
return NULL;
buffer = usb_alloc_coherent(udev, size, GFP_KERNEL, &urb->transfer_dma);
if (buffer == NULL)
goto error;
/* initialize the urb properly */
usb_fill_bulk_urb(urb, udev, pipe, buffer, size, callback, curb);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
return urb;
error:
usb_free_urb(urb);
return NULL;
}
static struct cpad_urb *cpad_urb_alloc(struct syndisplay *display, int out_size)
{
struct usb_device *udev = display->parent->udev;
struct cpad_urb *curb;
curb = kzalloc(sizeof(*curb), GFP_KERNEL);
if (curb == NULL) {
cpad_err(display, "Out of memory in cpad_urb_alloc");
return NULL;
}
curb->display = display;
curb->out = cpad_alloc_bulk(curb, out_size, cpad_out_callback,
usb_sndbulkpipe(udev, display->out_endpointAddr));
if (curb->out == NULL)
goto error;
curb->in = cpad_alloc_bulk(curb, CPAD_PACKET_SIZE, cpad_in_callback,
usb_rcvbulkpipe(udev, display->in_endpointAddr));
if (curb->in == NULL)
goto error;
return curb;
error:
cpad_err(display, "Out of memory in cpad_alloc_bulk");
cpad_urb_free(curb);
return NULL;
}
/*
* cPad display character device code
*/
MODULE_PARM_DESC(exclusive_open,
"if set, cPad character device can only be opened by one program at a time");
static int exclusive_open;
module_param(exclusive_open, int, 0644);
static int cpad_open(struct inode *inode, struct file *file)
{
struct synusb *synusb;
struct syndisplay *display;
struct usb_interface *interface;
int subminor;
int retval = 0;
subminor = iminor(inode);
interface = usb_find_interface(&synusb_driver, subminor);
if (interface == NULL) {
err("%s - error, can't find device for minor %d",
__func__, subminor);
return -ENODEV;
}
synusb = usb_get_intfdata(interface);
if (synusb == NULL)
return -ENODEV;
display = synusb->display;
/* increment our usage count for the device */
kref_get(&synusb->kref);
/* the io_mutex is needed in suspend and resume, so we need a different
* mutex here */
mutex_lock(&display->open_mutex);
if (!display->open_count++) {
/* prevent the device from being autosuspended */
retval = usb_autopm_get_interface(interface);
if (retval)
goto error;
} else if (exclusive_open) {
retval = -EBUSY;
goto error;
}
/* save our object in the file's private structure */
file->private_data = display;
mutex_unlock(&display->open_mutex);
return retval;
error:
display->open_count--;
mutex_unlock(&display->open_mutex);
kref_put(&synusb->kref, synusb_delete);
return retval;
}
static int cpad_release(struct inode *inode, struct file *file)
{
struct synusb *synusb;
struct syndisplay *display;
display = (struct syndisplay *)file->private_data;
if (display == NULL)
return -ENODEV;
synusb = display->parent;
/* allow the device to be autosuspended */
mutex_lock(&display->open_mutex);
if (!--display->open_count && !display->gone)
usb_autopm_put_interface(synusb->interface);
mutex_unlock(&display->open_mutex);
/* decrement the count on our device */
kref_put(&synusb->kref, synusb_delete);
return 0;
}
static int cpad_flush(struct file *file, fl_owner_t id)
{
struct syndisplay *display;
int res;
display = (struct syndisplay *)file->private_data;
if (display == NULL)
return -ENODEV;
/* wait for io to stop */
res = cpad_lock(display, file);
if (!res)
cpad_unlock(display);
return res;
}
static ssize_t cpad_read(struct file *file, char *buffer,
size_t count, loff_t *ppos)
{
struct syndisplay *display;
int retval;
size_t bytes_read;
display = (struct syndisplay *)file->private_data;
retval = cpad_lock(display, file);
if (retval)
return retval;
if (!display->has_indata) {
retval = -ENODATA;
goto error;
}
/* copy the data to userspace */
bytes_read = min(count, (size_t)display->user_urb->in->actual_length);
if (copy_to_user(buffer, display->user_urb->in->transfer_buffer,
bytes_read))
retval = -EFAULT;
else
retval = bytes_read;
error:
cpad_unlock(display);
return retval;
}
static inline u8 *cpad_1335_fillpacket(u8 cmd, u8 *param,
size_t param_size, u8 *out_buf)
{
u8 *point;
/* select sed1335, set sed1335 command, reverse parameters */
*(out_buf++) = SEL_1335;
*(out_buf++) = cmd;
for (point = param+param_size-1; point >= param; point--)
*(out_buf++) = *point;
return out_buf;
}
static ssize_t cpad_write_fillbuffer(struct syndisplay *display,
const u8 *ubuffer, size_t count)
{
struct urb *out = display->user_urb->out;
u8 *out_buf = out->transfer_buffer;
const u8 *ubuffer_orig = ubuffer;
u8 param[CPAD_MAX_PARAM_SIZE];
size_t param_size, actual_length;
u8 cmd;
/* get 1335 command first */
if (get_user(cmd, ubuffer))
return -EFAULT;
ubuffer++;
if (cmd == SLEEP_1335)
cpad_warn(display, "Sleeping sed1335 might "
"damage the display");
if (count == 1) {
/* 1335 command without params */
*(out_buf++) = SEL_1335;
*(out_buf++) = cmd;
actual_length = CPAD_COMMAND_SIZE;
goto exit;
}
actual_length = 0;
count--;
while (count > 0) {
param_size = min(count, (size_t)CPAD_MAX_PARAM_SIZE);
if (actual_length+param_size+CPAD_COMMAND_SIZE >
CPAD_MAX_TRANSFER)
break;
if (copy_from_user(param, ubuffer, param_size))
return -EFAULT;
ubuffer += param_size;
count -= param_size;
out_buf = cpad_1335_fillpacket(cmd, param, param_size, out_buf);
actual_length += param_size + CPAD_COMMAND_SIZE;
}
exit:
out->transfer_buffer_length = actual_length;
return (ssize_t)(ubuffer-ubuffer_orig);
}
static ssize_t cpad_write(struct file *file, const char *user_buffer,
size_t count, loff_t *ppos)
{
struct syndisplay *display;
ssize_t retval = 0;
ssize_t writesize;
display = (struct syndisplay *)file->private_data;
/* verify that we actually have some data to write */
if (count == 0)
return 0;
retval = (ssize_t)cpad_lock(display, file);
if (retval)
return retval;
writesize = cpad_write_fillbuffer(display, (u8 *) user_buffer, count);
if (writesize < 0) {
retval = writesize;
goto error;
}
retval = (ssize_t)cpad_submit_user(display);
if (!retval)
retval = writesize;
error:
cpad_unlock(display);
return retval;
}
/* this function is scheduled as a delayed_work, initiated in cpad_flash */
static void cpad_light_off(struct work_struct *work)
{
struct syndisplay *display = container_of(work, struct syndisplay,
flash.work);
mutex_lock(&display->io_mutex);
if (display->gone || display->suspended ||
display->reset_in_progress || display->reset_notify)
return;
if (!display->backlight_on)
return;
if (cpad_nlcd_function(display, CPAD_W_LIGHT, 0, CPAD_NLCD_URB) < 0)
cpad_err(display, "error on cpad_light_off");
mutex_unlock(&display->io_mutex);
}
/* helper function for the backlight flash ioctl */
static int cpad_flash(struct syndisplay *display, int time)
{
struct delayed_work *flash = &display->flash;
int res;
if (time <= 0)
return -EINVAL;
cancel_delayed_work(flash);
res = cpad_nlcd_function(display, CPAD_W_LIGHT, 1, CPAD_USER_URB);
if (res)
return res;
time = min(time, 1000);
schedule_delayed_work(flash, msecs_to_jiffies(10*time));
return 0;
}
static int cpad_driver_num = CPAD_DRIVER_NUM;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
static long cpad_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#else
static int cpad_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) */
{
struct syndisplay *display;
u8 cval = 0;
int ival = 0;
void *rval = NULL;
int res = 0;
display = (struct syndisplay *)file->private_data;
res = cpad_lock(display, file);
if (res)
return res;
/* read data from user */
if (cmd & IOC_IN) {
res = -EFAULT;
switch (_IOC_SIZE(cmd)) {
case sizeof(u8):
if (get_user(cval, (u8 *) arg))
goto error;
break;
case sizeof(int):
if (get_user(ival, (int *) arg))
goto error;
break;
default:
res = -ENOIOCTLCMD;
goto error;
}
res = 0;
}
switch (cmd) {
case CPAD_VERSION:
rval = &cpad_driver_num;
break;
case CPAD_CGID:
rval = &display->parent->idev->id;
break;
case CPAD_WLIGHT:
res = cpad_nlcd_function(display, CPAD_W_LIGHT,
cval, CPAD_USER_URB);
break;
case CPAD_FLASH:
res = cpad_flash(display, ival);
break;
case CPAD_WLCD:
res = cpad_nlcd_function(display, CPAD_W_LCD,
cval, CPAD_USER_URB);
break;
case CPAD_RLIGHT:
res = cpad_nlcd_function(display, CPAD_R_LIGHT,
0, CPAD_USER_URB);
break;
case CPAD_RLCD:
res = cpad_nlcd_function(display, CPAD_R_LCD,
0, CPAD_USER_URB);
break;
case CPAD_RESET:
cpad_err(display, "CPAD_RESET ioctl is deprecated. "
"Use libusb instead.");
res = -ENOIOCTLCMD;
break;
case CPAD_REEPROM:
res = cpad_nlcd_function(display, CPAD_R_ROM, 0, CPAD_USER_URB);
break;
default:
res = -ENOIOCTLCMD;
}
error:
cpad_unlock(display);
if (res < 0)
goto exit;
/* write data to user */
if ((cmd & IOC_OUT) && (rval != NULL))
if (copy_to_user((void *) arg, rval, _IOC_SIZE(cmd)))
res = -EFAULT;
exit:
return res < 0 ? res : 0;
}
static const struct file_operations cpad_fops = {
.owner = THIS_MODULE,
.read = cpad_read,
.write = cpad_write,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
.unlocked_ioctl = cpad_ioctl,
.compat_ioctl = cpad_ioctl,
#else
.ioctl = cpad_ioctl,
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) */
.open = cpad_open,
.release = cpad_release,
.flush = cpad_flush,
};
/*
* 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 cpad_class = {
.name = "usb/cpad%d",
.fops = &cpad_fops,
.minor_base = USB_CPAD_MINOR_BASE,
};
/*
* initialization and removal of data structures
*/
int cpad_alloc(struct synusb *synusb)
{
if (!synusb->has_display)
return 0;
synusb->display = kzalloc(sizeof(*(synusb->display)), GFP_KERNEL);
if (synusb->display == NULL) {
dev_err(&synusb->interface->dev,
"Out of memory in cpad_alloc\n");
return -ENOMEM;
}
synusb->display->parent = synusb;
return 0;
}
void cpad_free(struct synusb *synusb)
{
struct syndisplay *display = synusb->display;
if (display == NULL)
return;
/* restore the allocated buffer length for usb_free_coherent in
* synusb_free_urb to work correctly */
if (display->user_urb)
if (display->user_urb->out)
display->user_urb->out->transfer_buffer_length =
CPAD_MAX_TRANSFER;
cpad_urb_free(display->user_urb);
cpad_urb_free(display->nlcd_urb);
kfree(display);
synusb->display = NULL;
}
static void cpad_disable(struct synusb *synusb)
{
cpad_warn(synusb->display, "Disabling cPad display character device");
cpad_free(synusb);
}
int cpad_setup_in(struct synusb *synusb,
struct usb_endpoint_descriptor *endpoint)
{
struct syndisplay *display = synusb->display;
if (display == NULL)
return 0;
if ((display->in_endpointAddr) ||
(endpoint->wMaxPacketSize != CPAD_PACKET_SIZE)) {
cpad_disable(synusb);
return 0;
}
display->in_endpointAddr = endpoint->bEndpointAddress;
return 0;
}
int cpad_setup_out(struct synusb *synusb,
struct usb_endpoint_descriptor *endpoint)
{
struct syndisplay *display = synusb->display;
if (display == NULL)
return 0;
if ((display->out_endpointAddr) ||
(endpoint->wMaxPacketSize != CPAD_PACKET_SIZE)) {
cpad_disable(synusb);
return 0;
}
display->out_endpointAddr = endpoint->bEndpointAddress;
return 0;
}
int cpad_check_setup(struct synusb *synusb)
{
if (synusb->display == NULL)
return 0;
if ((synusb->display->in_endpointAddr) &&
(synusb->display->out_endpointAddr))
return 0;
/* if the cPad display endpoints can not be set up correctly,
* just disable the display */
cpad_disable(synusb);
return 0;
}
int cpad_init(struct synusb *synusb)
{
struct syndisplay *display = synusb->display;
int retval;
if (display == NULL)
return 0;
display->user_urb = cpad_urb_alloc(display, CPAD_MAX_TRANSFER);
/* the nlcd urb needs only one byte of parameters */
display->nlcd_urb = cpad_urb_alloc(display, CPAD_COMMAND_SIZE+1);
if (!(display->user_urb && display->nlcd_urb)) {
retval = -ENOMEM;
goto error;
}
mutex_init(&display->io_mutex);
mutex_init(&display->open_mutex);
init_completion(&display->done);
INIT_DELAYED_WORK(&display->flash, cpad_light_off);
retval = usb_register_dev(synusb->interface, &cpad_class);
if (retval) {
cpad_err(display, "Not able to get a minor for this device");
goto error;
}
/* let the user know what node this device is now attached to */
dev_info(&synusb->interface->dev, "cPad device now attached to cpad-%d",
synusb->interface->minor);
return 0;
error:
cpad_disable(synusb);
return 0;
}
void cpad_disconnect(struct synusb *synusb)
{
struct syndisplay *display = synusb->display;
if (display == NULL)
return;
/* give back our minor */
usb_deregister_dev(synusb->interface, &cpad_class);
/* prevent more I/O from starting */
mutex_lock(&display->open_mutex);
mutex_lock(&display->io_mutex);
display->gone = 1;
mutex_unlock(&display->io_mutex);
mutex_unlock(&display->open_mutex);
cancel_delayed_work(&display->flash);
/* wait for all callbacks of display->flash to finish */
flush_scheduled_work();
}
/*
* suspend code
*/
int cpad_suspend(struct synusb *synusb)
{
struct syndisplay *display = synusb->display;
int retval = 0;
if (display == NULL)
return 0;
if (mutex_lock_interruptible(&display->io_mutex))
return -ERESTARTSYS;
cancel_delayed_work(&display->flash);
if (display->backlight_on) {
retval = cpad_nlcd_function(display, CPAD_W_LIGHT,
0, CPAD_NLCD_URB);
if (retval < 0)
cpad_err(display, "Could not turn backlight "
"off before suspend");
else
retval = 0;
}
if (retval == 0)
display->suspended = 1;
mutex_unlock(&display->io_mutex);
return retval;
}
int cpad_resume(struct synusb *synusb)
{
struct syndisplay *display = synusb->display;
if (display == NULL)
return 0;
mutex_lock(&display->io_mutex);
display->suspended = 0;
mutex_unlock(&display->io_mutex);
return 0;
}
/* set cPad status variables for a reset */
static inline void cpad_set_reset(struct syndisplay *display)
{
display->reset_notify = 1;
display->has_indata = 0;
display->backlight_on = 0;
}
int cpad_pre_reset(struct synusb *synusb)
{
struct syndisplay *display = synusb->display;
if (display == NULL)
return 0;
if (mutex_lock_interruptible(&display->io_mutex))
return -ERESTARTSYS;
/* we do not need to switch off the backlight here */
cancel_delayed_work(&synusb->display->flash);
display->reset_in_progress = 1;
cpad_set_reset(display);
mutex_unlock(&display->io_mutex);
return 0;
}
int cpad_post_reset(struct synusb *synusb)
{
struct syndisplay *display = synusb->display;
if (display == NULL)
return 0;
mutex_lock(&display->io_mutex);
display->reset_in_progress = 0;
mutex_unlock(&display->io_mutex);
return 0;
}
int cpad_reset_resume(struct synusb *synusb)
{
struct syndisplay *display = synusb->display;
if (display == NULL)
return 0;
mutex_lock(&display->io_mutex);
display->suspended = 0;
cpad_set_reset(display);
mutex_unlock(&display->io_mutex);
return 0;
}

127
drivers/input/mouse/cpad.h Normal file
View File

@@ -0,0 +1,127 @@
#ifndef _CPAD_H
#define _CPAD_H
#ifdef CONFIG_MOUSE_SYNAPTICS_CPADDEV
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include "synapticsusb.h"
#define USB_CPAD_MINOR_BASE 192
/* packet size of in and out bulk endpoints */
#define CPAD_PACKET_SIZE 32
/* a packet consists of a command and optional parameters */
#define CPAD_COMMAND_SIZE 2
#define CPAD_MAX_PARAM_SIZE (CPAD_PACKET_SIZE-CPAD_COMMAND_SIZE)
/* One packet (32 bytes) is needed to write 30 bytes to the display RAM.
* The cPad has 8k (=30*273+2) bytes of RAM, so the maximum useful number
* of packets in an URB is 274. Notice that the transfer buffer is re-used. */
#define CPAD_MAX_TRANSFER (274*CPAD_PACKET_SIZE)
/* possible values for the first byte of a packet */
#define SEL_CPAD 0x01 /* select a non-lcd-controller function */
#define SEL_1335 0x02 /* run sed1335 command, see linux/cpad.h */
/* observed non-lcd-controller functions */
#define CPAD_W_ROM 0x01 /* write EEPROM, not supported by this driver */
#define CPAD_R_ROM 0x02 /* read EEPROM */
#define CPAD_W_LIGHT 0x03 /* write backlight state (on/off) */
#define CPAD_R_LIGHT 0x04 /* read backlight state (on/off) */
#define CPAD_W_LCD 0x05 /* write lcd state (on/off) */
#define CPAD_R_LCD 0x06 /* read lcd state (on/off) */
#define CPAD_RSRVD 0x07
/* struct syndisplay uses its parents kref for reference counting */
struct syndisplay {
struct synusb *parent;
/* synchronize open/close against disconnect */
struct mutex open_mutex;
/* locked by open_mutex */
int open_count;
/* synchronize io, locks all except open_mutex and open_count */
struct mutex io_mutex;
/* wait for callbacks */
struct completion done;
/* report error in callback */
int error;
/* urb initiated by user, is re-used */
struct cpad_urb *user_urb;
/* used by the driver, e.g., to switch off the backlight */
struct cpad_urb *nlcd_urb;
/* flash backlight */
struct delayed_work flash;
__u8 in_endpointAddr;
__u8 out_endpointAddr;
/* status of the device */
unsigned int gone:1;
unsigned int has_indata:1;
unsigned int reset_notify:1;
unsigned int reset_in_progress:1;
unsigned int suspended:1;
unsigned int backlight_on:1;
};
#define CPAD_USER_URB 0 /* use syndisplay.user_urb */
#define CPAD_NLCD_URB 1 /* use syndisplay.nlcd_urb */
/* An urb to the bulk out endpoint should be followed by an urb to the bulk in
* endpoint. Both urbs are collected in this structure. */
struct cpad_urb {
struct syndisplay *display;
struct urb *in, *out;
};
/* helper functions for printk */
#define cpad_err(disp, format, arg...) \
dev_err(&(disp)->parent->interface->dev, format "\n", ## arg)
#define cpad_warn(disp, format, arg...) \
dev_warn(&(disp)->parent->interface->dev, format "\n", ## arg)
/* functions called from synapticsusb.c */
int cpad_alloc(struct synusb *);
void cpad_free(struct synusb *);
int cpad_setup_in(struct synusb *, struct usb_endpoint_descriptor *);
int cpad_setup_out(struct synusb *, struct usb_endpoint_descriptor *);
int cpad_check_setup(struct synusb *);
int cpad_init(struct synusb *);
void cpad_disconnect(struct synusb *);
int cpad_suspend(struct synusb *);
int cpad_resume(struct synusb *);
int cpad_pre_reset(struct synusb *);
int cpad_post_reset(struct synusb *);
int cpad_reset_resume(struct synusb *);
#else /* CONFIG_MOUSE_SYNAPTICS_CPADDEV */
#include "synapticsusb.h"
struct syndisplay { };
static inline int cpad_alloc(struct synusb *synusb) { return 0; }
void cpad_free(struct synusb *synusb) { }
static inline int cpad_setup_in(struct synusb *synusb,
struct usb_endpoint_descriptor *endpoint) { return 0; }
static inline int cpad_setup_out(struct synusb *synusb,
struct usb_endpoint_descriptor *endpoint) { return 0; }
static inline int cpad_check_setup(struct synusb *synusb) { return 0; }
static inline int cpad_init(struct synusb *synusb) { return 0; }
static inline void cpad_disconnect(struct synusb *synusb) { }
int cpad_suspend(struct synusb *synusb) { return 0; }
int cpad_resume(struct synusb *synusb) { return 0; }
int cpad_pre_reset(struct synusb *synusb) { return 0; }
int cpad_post_reset(struct synusb *synusb) { return 0; }
int cpad_reset_resume(struct synusb *synusb) { return 0; }
#endif /* CONFIG_MOUSE_SYNAPTICS_CPADDEV */
#endif /* _CPAD_H */

View File

@@ -0,0 +1,666 @@
/****
* USB Synaptics device driver
*
* Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk)
* Copyright (c) 2003 Ron Lee (ron@debian.org)
* cPad driver for kernel 2.4
*
* Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de)
* Copyright (c) 2004 Ron Lee (ron@debian.org)
* rewritten for kernel 2.6
*
* cPad dispaly character device part now in cpad.c
*
* Bases on: usb_skeleton.c v2.2 by Greg Kroah-Hartman
* drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik
* drivers/input/mouse/synaptics.c by Peter Osterlund
*
* 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.
*
* Trademarks are the property of their respective owners.
*/
/****
* There are three different types of Synaptics USB devices: Touchpads,
* touchsticks (or trackpoints), and touchscreens. Touchpads are well supported
* by this driver, touchstick support has not been tested much yet, and
* touchscreens have not been tested at all. The only difference between pad
* and stick seems to be that the x and y finger positions are unsigned 13 bit
* integers in the first case, but are signed ones in the second case.
*
* Up to three alternate settings are possible:
* setting 0: one int endpoint for relative movement (used by usbhid.ko)
* setting 1: one int endpoint for absolute finger position
* setting 2 (cPad only): one int endpoint for absolute finger position and
* two bulk endpoints for the display (in/out)
* This driver uses setting 2 for the cPad and setting 1 for other devices.
*
* The cPad is an USB touchpad with background display. The display driver part
* can be found in cpad.c.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kref.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/usb/input.h>
#include <linux/workqueue.h>
#include "synapticsusb.h"
#include "cpad.h"
/*
* input device code
*/
MODULE_PARM_DESC(xmin, "minimal horizontal finger position for touchpads");
MODULE_PARM_DESC(xmax, "maximal horizontal finger position for touchpads");
MODULE_PARM_DESC(ymin, "minimal vertical finger position for touchpads");
MODULE_PARM_DESC(ymax, "maximal vertical finger position for touchpads");
static int xmin = 1472;
static int xmax = 5472;
static int ymin = 1408;
static int ymax = 4448;
module_param(xmin, int, 0444);
module_param(xmax, int, 0444);
module_param(ymin, int, 0444);
module_param(ymax, int, 0444);
MODULE_PARM_DESC(btn_middle, "if set, cPad menu button is reported "
"as a middle button");
static int btn_middle = 1;
module_param(btn_middle, int, 0644);
static const char synusb_pad_name[] = "Synaptics USB TouchPad";
static const char synusb_stick_name[] = "Synaptics USB Styk";
static const char synusb_screen_name[] = "Synaptics USB TouchScreen";
static const char *synusb_get_name(struct synusb *synusb)
{
switch (synusb->input_type) {
case SYNUSB_PAD:
return synusb_pad_name;
case SYNUSB_STICK:
return synusb_stick_name;
case SYNUSB_SCREEN:
return synusb_screen_name;
}
return NULL;
}
/* report tool_width for touchpads */
static void synusb_report_width(struct input_dev *idev, int pressure, int w)
{
int num_fingers, tool_width;
if (pressure > 0) {
num_fingers = 1;
tool_width = 5;
switch (w) {
case 0 ... 1:
num_fingers = 2 + w;
break;
case 2: /* pen, pretend its a finger */
break;
case 4 ... 15:
tool_width = w;
break;
}
} else {
num_fingers = 0;
tool_width = 0;
}
input_report_abs(idev, ABS_TOOL_WIDTH, tool_width);
input_report_key(idev, BTN_TOOL_FINGER, num_fingers == 1);
input_report_key(idev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
input_report_key(idev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
}
/* convert signed or unsigned 13 bit number to int */
static inline int us13_to_int(u8 high, u8 low, int has_sign)
{
int res;
res = ((int)(high & 0x1f) << 8) | low;
if (has_sign && (high & 0x10))
res -= 0x2000;
return res;
}
static void synusb_input_callback(struct urb *urb)
{
struct synusb *synusb = (struct synusb *)urb->context;
u8 *data = urb->transfer_buffer;
struct input_dev *idev = synusb->idev;
int res, x, y, pressure;
int is_stick = (synusb->input_type == SYNUSB_STICK) ? 1 : 0;
if (urb->status) {
if (synusb_urb_status_error(urb)) {
synusb_err(synusb, "nonzero int in status received: %d",
urb->status);
/* an error occured, try to resubmit */
goto resubmit;
}
/* unlink urb, do not resubmit */
return;
}
pressure = data[6];
x = us13_to_int(data[2], data[3], is_stick);
y = us13_to_int(data[4], data[5], is_stick);
if (is_stick) {
y = -y;
if (pressure > 6)
input_report_key(idev, BTN_TOUCH, 1);
if (pressure < 5)
input_report_key(idev, BTN_TOUCH, 0);
input_report_key(idev, BTN_TOOL_FINGER, pressure ? 1 : 0);
} else {
if (pressure > 30)
input_report_key(idev, BTN_TOUCH, 1);
if (pressure < 25)
input_report_key(idev, BTN_TOUCH, 0);
synusb_report_width(idev, pressure, data[0] & 0x0f);
y = ymin + ymax - y;
}
if (pressure > 0) {
input_report_abs(idev, ABS_X, x);
input_report_abs(idev, ABS_Y, y);
}
input_report_abs(idev, ABS_PRESSURE, pressure);
input_report_key(idev, BTN_LEFT, data[1] & 0x04);
input_report_key(idev, BTN_RIGHT, data[1] & 0x01);
input_report_key(idev, BTN_MIDDLE, data[1] & 0x02);
if (synusb->has_display)
input_report_key(idev, btn_middle ? BTN_MIDDLE : BTN_MISC,
data[1] & 0x08);
input_sync(idev);
resubmit:
res = usb_submit_urb(urb, GFP_ATOMIC);
if (res)
synusb_err(synusb, "submit int in urb failed with result %d",
res);
}
/* Data must always be fetched from the int endpoint, otherwise the device
* would reconnect to force driver reload. So this is always scheduled by probe.
*/
static void synusb_submit_int(struct work_struct *work)
{
struct synusb *synusb = container_of(work, struct synusb, isubmit.work);
int res;
res = usb_submit_urb(synusb->iurb, GFP_KERNEL);
if (res)
synusb_err(synusb, "submit int in urb failed with result %d",
res);
}
static int synusb_init_input(struct synusb *synusb)
{
struct input_dev *idev;
struct usb_device *udev = synusb->udev;
int is_stick = (synusb->input_type == SYNUSB_STICK) ? 1 : 0;
int retval = -ENOMEM;
idev = input_allocate_device();
if (idev == NULL) {
synusb_err(synusb, "Can not allocate input device");
goto error;
}
__set_bit(EV_ABS, idev->evbit);
__set_bit(EV_KEY, idev->evbit);
if (is_stick) {
input_set_abs_params(idev, ABS_X, -512, 512, 0, 0);
input_set_abs_params(idev, ABS_Y, -512, 512, 0, 0);
input_set_abs_params(idev, ABS_PRESSURE, 0, 127, 0, 0);
} else {
input_set_abs_params(idev, ABS_X, xmin, xmax, 0, 0);
input_set_abs_params(idev, ABS_Y, ymin, ymax, 0, 0);
input_set_abs_params(idev, ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(idev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
__set_bit(BTN_TOOL_DOUBLETAP, idev->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, idev->keybit);
}
__set_bit(BTN_TOUCH, idev->keybit);
__set_bit(BTN_TOOL_FINGER, idev->keybit);
__set_bit(BTN_LEFT, idev->keybit);
__set_bit(BTN_RIGHT, idev->keybit);
__set_bit(BTN_MIDDLE, idev->keybit);
if (synusb->has_display)
__set_bit(BTN_MISC, idev->keybit);
usb_make_path(udev, synusb->iphys, sizeof(synusb->iphys));
strlcat(synusb->iphys, "/input0", sizeof(synusb->iphys));
idev->phys = synusb->iphys;
idev->name = synusb_get_name(synusb);
usb_to_input_id(udev, &idev->id);
idev->dev.parent = &synusb->interface->dev;
input_set_drvdata(idev, synusb);
retval = input_register_device(idev);
if (retval) {
synusb_err(synusb, "Can not register input device");
goto error;
}
synusb->idev = idev;
synusb->interface->needs_remote_wakeup = 1;
return 0;
error:
if (idev)
input_free_device(idev);
return retval;
}
/*
* initialization of usb data structures
*/
static int synusb_setup_iurb(struct synusb *synusb,
struct usb_endpoint_descriptor *endpoint)
{
char *buf;
if (endpoint->wMaxPacketSize < 8)
return 0;
if (synusb->iurb) {
synusb_warn(synusb, "Found more than one possible "
"int in endpoint");
return 0;
}
synusb->iurb = usb_alloc_urb(0, GFP_KERNEL);
if (synusb->iurb == NULL)
return -ENOMEM;
buf = usb_buffer_alloc(synusb->udev, 8, GFP_ATOMIC,
&synusb->iurb->transfer_dma);
if (buf == NULL)
return -ENOMEM;
usb_fill_int_urb(synusb->iurb, synusb->udev,
usb_rcvintpipe(synusb->udev,
endpoint->bEndpointAddress),
buf, 8, synusb_input_callback,
synusb, endpoint->bInterval);
synusb->iurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
return 0;
}
static int synusb_check_int_setup(struct synusb *synusb)
{
if (synusb->iurb)
return 0;
return -ENODEV;
}
static struct synusb_endpoint_table {
__u8 dir;
__u8 xfer_type;
int (*setup)(struct synusb *,
struct usb_endpoint_descriptor *);
} synusb_endpoints[] = {
{ USB_DIR_IN, USB_ENDPOINT_XFER_BULK, cpad_setup_in },
{ USB_DIR_OUT, USB_ENDPOINT_XFER_BULK, cpad_setup_out },
{ USB_DIR_IN, USB_ENDPOINT_XFER_INT, synusb_setup_iurb },
{ }
};
/* return entry index in synusb_endpoint_table that matches ep */
static inline int synusb_match_endpoint(struct usb_endpoint_descriptor *ep)
{
int i;
for (i = 0; synusb_endpoints[i].setup; i++)
if (((ep->bEndpointAddress & USB_DIR_IN)
== synusb_endpoints[i].dir) &&
((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== synusb_endpoints[i].xfer_type))
return i;
return -ENODEV;
}
static int synusb_setup_endpoints(struct synusb *synusb)
{
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int int_num = synusb->interface->cur_altsetting->desc.bInterfaceNumber;
unsigned altsetting;
int i, j, res;
altsetting = min((unsigned) (synusb->has_display ? 2 : 1),
synusb->interface->num_altsetting);
res = usb_set_interface(synusb->udev, int_num, altsetting);
if (res) {
synusb_err(synusb, "Can not set alternate setting to %i, "
"error: %i", altsetting, res);
return res;
}
/* allocate synusb->display, if the device has a display */
res = cpad_alloc(synusb);
if (res)
return res;
/* go through all endpoints and call the setup function
* listed in synusb_endpoint_table */
iface_desc = synusb->interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
j = synusb_match_endpoint(endpoint);
if (j >= 0) {
res = synusb_endpoints[j].setup(synusb, endpoint);
if (res)
return res;
}
}
/* check whether all possible endpoints have been set up */
res = synusb_check_int_setup(synusb);
if (res)
return res;
res = cpad_check_setup(synusb);
if (res)
return res;
return 0;
}
/* disable experimental stick support by default */
MODULE_PARM_DESC(enable_stick, "enable trackpoint support");
static int enable_stick;
module_param(enable_stick, int, 0644);
static int synusb_detect_type(struct synusb *synusb,
const struct usb_device_id *id)
{
int int_num = synusb->interface->cur_altsetting->desc.bInterfaceNumber;
synusb->has_display = 0;
if (id->idVendor == USB_VID_SYNAPTICS) {
switch (id->idProduct) {
case USB_DID_SYN_TS:
synusb->input_type = SYNUSB_SCREEN;
break;
case USB_DID_SYN_STICK:
synusb->input_type = SYNUSB_STICK;
break;
case USB_DID_SYN_COMP_TP:
if (int_num == 1)
synusb->input_type = SYNUSB_STICK;
else
synusb->input_type = SYNUSB_PAD;
break;
case USB_DID_SYN_CPAD:
synusb->has_display = 1;
default:
synusb->input_type = SYNUSB_PAD;
}
} else {
synusb->input_type = SYNUSB_PAD;
}
if ((synusb->input_type == SYNUSB_STICK) && !enable_stick)
return -ENODEV;
if (synusb->input_type == SYNUSB_SCREEN)
synusb_warn(synusb, "driver has not been tested "
"with touchscreens!");
return 0;
}
void synusb_free_urb(struct urb *urb)
{
if (urb == NULL)
return;
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
usb_free_urb(urb);
}
void synusb_delete(struct kref *kref)
{
struct synusb *synusb = container_of(kref, struct synusb, kref);
synusb_free_urb(synusb->iurb);
if (synusb->idev)
input_unregister_device(synusb->idev);
cpad_free(synusb);
usb_put_dev(synusb->udev);
kfree(synusb);
}
static int synusb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct synusb *synusb = NULL;
struct usb_device *udev = interface_to_usbdev(interface);
int retval = -ENOMEM;
synusb = kzalloc(sizeof(*synusb), GFP_KERNEL);
if (synusb == NULL) {
dev_err(&interface->dev, "Out of memory in synusb_probe\n");
goto error;
}
synusb->udev = usb_get_dev(udev);
synusb->interface = interface;
kref_init(&synusb->kref);
usb_set_intfdata(interface, synusb);
if (synusb_detect_type(synusb, id))
goto error;
retval = synusb_setup_endpoints(synusb);
if (retval) {
synusb_err(synusb, "Can not set up endpoints, error: %i",
retval);
goto error;
}
retval = synusb_init_input(synusb);
if (retval)
goto error;
retval = cpad_init(synusb);
if (retval)
goto error;
/* submit the int in urb */
INIT_DELAYED_WORK(&synusb->isubmit, synusb_submit_int);
schedule_delayed_work(&synusb->isubmit, msecs_to_jiffies(10));
return 0;
error:
if (synusb) {
usb_set_intfdata(interface, NULL);
kref_put(&synusb->kref, synusb_delete);
}
return retval;
}
static void synusb_disconnect(struct usb_interface *interface)
{
struct synusb *synusb;
synusb = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
cpad_disconnect(synusb);
cancel_delayed_work_sync(&synusb->isubmit);
usb_kill_urb(synusb->iurb);
input_unregister_device(synusb->idev);
synusb->idev = NULL;
kref_put(&synusb->kref, synusb_delete);
dev_info(&interface->dev, "Synaptics device disconnected\n");
}
/*
* suspend code
*/
static void synusb_draw_down(struct synusb *synusb)
{
cancel_delayed_work_sync(&synusb->isubmit);
usb_kill_urb(synusb->iurb);
}
static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct synusb *synusb = usb_get_intfdata(intf);
int res;
if (synusb == NULL)
return 0;
res = cpad_suspend(synusb);
if (res)
goto error;
synusb_draw_down(synusb);
error:
return res;
}
static int synusb_resume(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
int res;
res = usb_submit_urb(synusb->iurb, GFP_ATOMIC);
if (res) {
synusb_err(synusb, "submit int in urb failed during resume "
"with result %d", res);
goto error;
}
res = cpad_resume(synusb);
if (res)
synusb_draw_down(synusb);
error:
return res;
}
static int synusb_pre_reset(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
int res;
res = cpad_pre_reset(synusb);
if (res)
goto error;
synusb_draw_down(synusb);
error:
return res;
}
static int synusb_post_reset(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
int res;
res = usb_submit_urb(synusb->iurb, GFP_ATOMIC);
if (res) {
synusb_err(synusb, "submit int in urb failed in during "
"post_reset with result %d", res);
goto error;
}
res = cpad_post_reset(synusb);
if (res)
synusb_draw_down(synusb);
error:
return res;
}
static int synusb_reset_resume(struct usb_interface *intf)
{
struct synusb *synusb = usb_get_intfdata(intf);
int res;
res = usb_submit_urb(synusb->iurb, GFP_ATOMIC);
if (res) {
synusb_err(synusb, "submit int in urb failed during "
"reset-resume with result %d", res);
goto error;
}
res = cpad_reset_resume(synusb);
if (res)
synusb_draw_down(synusb);
error:
return res;
}
/* the id table is filled via sysfs, so usbhid is always the default driver */
static struct usb_device_id synusb_idtable[] = { { } };
MODULE_DEVICE_TABLE(usb, synusb_idtable);
struct usb_driver synusb_driver = {
.name = "synaptics_usb",
.probe = synusb_probe,
.disconnect = synusb_disconnect,
.id_table = synusb_idtable,
.suspend = synusb_suspend,
.resume = synusb_resume,
.pre_reset = synusb_pre_reset,
.post_reset = synusb_post_reset,
.reset_resume = synusb_reset_resume,
.supports_autosuspend = 1,
};
static int __init synusb_init(void)
{
int result;
result = usb_register(&synusb_driver);
if (result)
err("usb_register failed. Error number %d", result);
else
pr_info(DRIVER_DESC " " DRIVER_VERSION "\n");
return result;
}
static void __exit synusb_exit(void)
{
usb_deregister(&synusb_driver);
}
module_init(synusb_init);
module_exit(synusb_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,78 @@
#ifndef _SYNAPTICS_USB_H
#define _SYNAPTICS_USB_H
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#define DRIVER_VERSION "v1.5"
#define DRIVER_AUTHOR "Rob Miller (rob@inpharmatica . co . uk), "\
"Ron Lee (ron@debian.org), "\
"Jan Steinhoff (cpad@jan-steinhoff . de)"
#define DRIVER_DESC "USB Synaptics device driver"
/* vendor and device IDs */
#define USB_VID_SYNAPTICS 0x06cb /* Synaptics vendor ID */
#define USB_DID_SYN_TP 0x0001 /* Synaptics USB TouchPad */
#define USB_DID_SYN_INT_TP 0x0002 /* Synaptics Integrated USB TouchPad */
#define USB_DID_SYN_CPAD 0x0003 /* Synaptics cPad */
#define USB_DID_SYN_TS 0x0006 /* Synaptics TouchScreen */
#define USB_DID_SYN_STICK 0x0007 /* Synaptics USB Styk */
#define USB_DID_SYN_WP 0x0008 /* Synaptics USB WheelPad */
#define USB_DID_SYN_COMP_TP 0x0009 /* Synaptics Composite USB TouchPad */
#define USB_DID_SYN_WTP 0x0010 /* Synaptics Wireless TouchPad */
#define USB_DID_SYN_DP 0x0013 /* Synaptics DisplayPad */
/* structure to hold all of our device specific stuff */
struct synusb {
struct usb_device *udev;
struct usb_interface *interface;
struct kref kref;
/* characteristics of the device */
unsigned int input_type:8;
unsigned int buttons:8;
unsigned int has_display:1;
/* input device related data structures */
struct input_dev *idev;
char iphys[64];
struct delayed_work isubmit;
struct urb *iurb;
/* cPad display character device, see cpad.h */
struct syndisplay *display;
};
/* possible values for synusb.input_type */
#define SYNUSB_PAD 0
#define SYNUSB_STICK 1
#define SYNUSB_SCREEN 2
extern struct usb_driver synusb_driver;
/* free urb and its buffer */
void synusb_free_urb(struct urb *urb);
/* cleanup handler for kref_put, kref_put(&synusb->kref, synusb_delete) */
void synusb_delete(struct kref *);
static inline int synusb_urb_status_error(struct urb *urb)
{
/* sync/async unlink faults aren't errors */
if (urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN)
return 0;
else
return urb->status;
}
/* helper functions for printk */
#define synusb_err(_synusb, format, arg...) \
dev_err(&(_synusb)->interface->dev, format "\n", ## arg)
#define synusb_warn(_synusb, format, arg...) \
dev_warn(&(_synusb)->interface->dev, format "\n", ## arg)
#include "cpad.h"
#endif /* _SYNAPTICS_USB_H */

87
include/linux/cpad.h Normal file
View File

@@ -0,0 +1,87 @@
#ifndef _LINUX_CPAD_H
#define _LINUX_CPAD_H
#include <linux/input.h>
/* Supported ioctls for /dev/usb/cpad*
* Because the backlight has a finite lifespan (ca. 1000 - 3000 hours), the
* CPAD_FLASH ioctl should be used instead of CPAD_WLIGHT.
* To get the result of CPAD_RLIGHT and CPAD_RLCD, read some bytes from
* the character device. The third byte is the result.
*/
#define CPAD_IOCTL_BASE 0x71
/* reported by CPAD_VERSION */
#define CPAD_DRIVER_NUM 8
/* get driver version */
#define CPAD_VERSION _IOR('U', CPAD_IOCTL_BASE, int)
/* get device ID */
#define CPAD_CGID _IOR('U', CPAD_IOCTL_BASE+1, struct input_id)
/* reset cPad device (DEPRECATED) */
#define CPAD_RESET _IO('U', CPAD_IOCTL_BASE+2)
/* set backlight state */
#define CPAD_WLIGHT _IOW('U', CPAD_IOCTL_BASE+3, u8)
/* set LCD state */
#define CPAD_WLCD _IOW('U', CPAD_IOCTL_BASE+5, u8)
/* read backlight state */
#define CPAD_RLIGHT _IO('U', CPAD_IOCTL_BASE+6)
/* read LCD state */
#define CPAD_RLCD _IO('U', CPAD_IOCTL_BASE+7)
/* flash backlight */
#define CPAD_FLASH _IOW('U', CPAD_IOCTL_BASE+8, int)
/* read eeprom */
#define CPAD_REEPROM _IO('U', CPAD_IOCTL_BASE+4)
/*
* writing to /dev/usb/cpad*
* The cPad display is controlled by a Seiko/Epson 1335 LCD Controller IC.
* A write to the device consists of a command followed by data:
* <1335 command> [<data> ...]
* For the MRWITE_1335 command, data may be up to 274*30 bytes long.
* Possible commands are listed below.
*
* reading /dev/usb/cpad*
* Reads answer of the sed1335 to a command, can be 2-32 bytes long.
* The information is only accessible until the next write access.
*/
/* Possible sed1335 commands as reported in its data sheet are: */
/* system control */
#define SYSSET_1335 0x40
#define SLEEP_1335 0x53
/* display control */
#define DISPOFF_1335 0x58
#define DISP_1335 0x59
#define SCROLL_1335 0x44
#define CSRF_1335 0x5d
#define CGRAMADR_1335 0x5c
#define CSRDIR_RIGHT_1335 0x4c
#define CSRDIR_LEFT_1335 0x4d
#define CSRDIR_UP_1335 0x4e
#define CSRDIR_DOWN_1335 0x4f
#define HDOTSCR_1335 0x5a
#define OVLAY_1335 0x5b
/* drawing control */
#define CSRW_1335 0x46
#define CSRR_1335 0x47
/* memory control */
#define MWRITE_1335 0x42
#define MREAD_1335 0x43
#endif /* _LINUX_CPAD_H */