mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-02-03 23:54:40 +00:00
synaptics-usb 1.5rc8 for hacking around with the touchpad (no mods made for detection yet)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
950
drivers/input/mouse/cpad.c
Normal 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
127
drivers/input/mouse/cpad.h
Normal 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 */
|
||||
666
drivers/input/mouse/synapticsusb.c
Normal file
666
drivers/input/mouse/synapticsusb.c
Normal 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");
|
||||
78
drivers/input/mouse/synapticsusb.h
Normal file
78
drivers/input/mouse/synapticsusb.h
Normal 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
87
include/linux/cpad.h
Normal 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 */
|
||||
Reference in New Issue
Block a user