848 lines
21 KiB
C
848 lines
21 KiB
C
/*
|
|
* Projector function driver
|
|
*
|
|
* Copyright (C) 2010 HTC Corporation
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/err.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <mach/msm_fb.h>
|
|
#include <linux/input.h>
|
|
|
|
#include <linux/usb/android_composite.h>
|
|
|
|
#ifdef DBG
|
|
#undef DBG
|
|
#endif
|
|
|
|
#if 1
|
|
#define DBG(x...) do {} while (0)
|
|
#else
|
|
#define DBG(x...) printk(KERN_INFO x)
|
|
#endif
|
|
|
|
/*16KB*/
|
|
#define TXN_MAX 16384
|
|
#define RXN_MAX 4096
|
|
|
|
/* number of rx and tx requests to allocate */
|
|
#define RX_REQ_MAX 4
|
|
#define TX_REQ_MAX 56 /*for 8k resolution 480*800*2 / 16k */
|
|
|
|
#define BITSPIXEL 16
|
|
#define PROJECTOR_FUNCTION_NAME "projector"
|
|
|
|
static int keypad_code[] = {KEY_WAKEUP, 0, 0, 0, KEY_HOME, KEY_MENU, KEY_BACK};
|
|
static const char shortname[] = "android_projector";
|
|
|
|
struct projector_dev {
|
|
struct usb_function function;
|
|
struct usb_composite_dev *cdev;
|
|
spinlock_t lock;
|
|
|
|
struct usb_ep *ep_in;
|
|
struct usb_ep *ep_out;
|
|
|
|
int online;
|
|
int error;
|
|
|
|
struct list_head tx_idle;
|
|
struct list_head rx_idle;
|
|
|
|
int rx_done;
|
|
|
|
u32 bitsPixel;
|
|
u32 framesize;
|
|
u32 width;
|
|
u32 height;
|
|
u8 init_done;
|
|
u8 enabled;
|
|
u16 frame_count;
|
|
struct input_dev *keypad_input;
|
|
struct input_dev *touch_input;
|
|
char *fbaddr;
|
|
struct platform_device *pdev;
|
|
};
|
|
|
|
static struct usb_interface_descriptor projector_interface_desc = {
|
|
.bLength = USB_DT_INTERFACE_SIZE,
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
.bInterfaceNumber = 0,
|
|
.bNumEndpoints = 2,
|
|
.bInterfaceClass = 0xFF,
|
|
.bInterfaceSubClass = 0xFF,
|
|
.bInterfaceProtocol = 0xFF,
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor projector_highspeed_in_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_IN,
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
.wMaxPacketSize = __constant_cpu_to_le16(512),
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor projector_highspeed_out_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_OUT,
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
.wMaxPacketSize = __constant_cpu_to_le16(512),
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor projector_fullspeed_in_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_IN,
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor projector_fullspeed_out_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_OUT,
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
};
|
|
|
|
static struct usb_descriptor_header *fs_projector_descs[] = {
|
|
(struct usb_descriptor_header *) &projector_interface_desc,
|
|
(struct usb_descriptor_header *) &projector_fullspeed_in_desc,
|
|
(struct usb_descriptor_header *) &projector_fullspeed_out_desc,
|
|
NULL,
|
|
};
|
|
|
|
static struct usb_descriptor_header *hs_projector_descs[] = {
|
|
(struct usb_descriptor_header *) &projector_interface_desc,
|
|
(struct usb_descriptor_header *) &projector_highspeed_in_desc,
|
|
(struct usb_descriptor_header *) &projector_highspeed_out_desc,
|
|
NULL,
|
|
};
|
|
|
|
/* string descriptors: */
|
|
|
|
static struct usb_string projector_string_defs[] = {
|
|
[0].s = "HTC PROJECTOR",
|
|
{ } /* end of list */
|
|
};
|
|
|
|
static struct usb_gadget_strings projector_string_table = {
|
|
.language = 0x0409, /* en-us */
|
|
.strings = projector_string_defs,
|
|
};
|
|
|
|
static struct usb_gadget_strings *projector_strings[] = {
|
|
&projector_string_table,
|
|
NULL,
|
|
};
|
|
static struct projector_dev _projector_dev;
|
|
struct device prj_dev;
|
|
|
|
static inline struct projector_dev *func_to_dev(struct usb_function *f)
|
|
{
|
|
return container_of(f, struct projector_dev, function);
|
|
}
|
|
|
|
|
|
static struct usb_request *projector_request_new(struct usb_ep *ep, int buffer_size)
|
|
{
|
|
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
|
|
if (!req)
|
|
return NULL;
|
|
|
|
/* now allocate buffers for the requests */
|
|
req->buf = kmalloc(buffer_size, GFP_KERNEL);
|
|
if (!req->buf) {
|
|
usb_ep_free_request(ep, req);
|
|
return NULL;
|
|
}
|
|
|
|
return req;
|
|
}
|
|
|
|
static void projector_request_free(struct usb_request *req, struct usb_ep *ep)
|
|
{
|
|
if (req) {
|
|
kfree(req->buf);
|
|
usb_ep_free_request(ep, req);
|
|
}
|
|
}
|
|
|
|
static inline int _lock(atomic_t *excl)
|
|
{
|
|
if (atomic_inc_return(excl) == 1) {
|
|
return 0;
|
|
} else {
|
|
atomic_dec(excl);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static inline void _unlock(atomic_t *excl)
|
|
{
|
|
atomic_dec(excl);
|
|
}
|
|
|
|
/* add a request to the tail of a list */
|
|
static void req_put(struct projector_dev *dev, struct list_head *head,
|
|
struct usb_request *req)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
list_add_tail(&req->list, head);
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
}
|
|
|
|
/* remove a request from the head of a list */
|
|
static struct usb_request *req_get(struct projector_dev *dev, struct list_head *head)
|
|
{
|
|
unsigned long flags;
|
|
struct usb_request *req;
|
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
if (list_empty(head)) {
|
|
req = 0;
|
|
} else {
|
|
req = list_first_entry(head, struct usb_request, list);
|
|
list_del(&req->list);
|
|
}
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
return req;
|
|
}
|
|
|
|
static void projector_queue_out(struct projector_dev *ctxt)
|
|
{
|
|
int ret;
|
|
struct usb_request *req;
|
|
|
|
/* if we have idle read requests, get them queued */
|
|
while ((req = req_get(ctxt, &ctxt->rx_idle))) {
|
|
req->length = RXN_MAX;
|
|
DBG("%s: queue %p\n", __func__, req);
|
|
ret = usb_ep_queue(ctxt->ep_out, req, GFP_ATOMIC);
|
|
if (ret < 0) {
|
|
DBG("projector: failed to queue out req (%d)\n", ret);
|
|
ctxt->error = 1;
|
|
req_put(ctxt, &ctxt->rx_idle, req);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* for mouse event type, 1 :move, 2:down, 3:up */
|
|
static void projector_send_touch_event(struct projector_dev *dev,
|
|
int iPenType, int iX, int iY)
|
|
{
|
|
struct input_dev *tdev = dev->touch_input;
|
|
static int b_prePenDown = false;
|
|
static int b_firstPenDown = true;
|
|
static int iCal_LastX;
|
|
static int iCal_LastY;
|
|
static int iReportCount;
|
|
|
|
if (iPenType != 3) {
|
|
if (b_firstPenDown) {
|
|
input_report_abs(tdev, ABS_X, iX);
|
|
input_report_abs(tdev, ABS_Y, iY);
|
|
input_report_abs(tdev, ABS_PRESSURE, 100);
|
|
input_report_abs(tdev, ABS_TOOL_WIDTH, 1);
|
|
input_report_key(tdev, BTN_TOUCH, 1);
|
|
input_report_key(tdev, BTN_2, 0);
|
|
input_sync(tdev);
|
|
b_firstPenDown = false;
|
|
b_prePenDown = true; /* For one pen-up only */
|
|
printk(KERN_INFO "projector: Pen down %d, %d\n", iX, iY);
|
|
} else {
|
|
/* don't report the same point */
|
|
if (iX != iCal_LastX || iY != iCal_LastY) {
|
|
input_report_abs(tdev, ABS_X, iX);
|
|
input_report_abs(tdev, ABS_Y, iY);
|
|
input_report_abs(tdev, ABS_PRESSURE, 100);
|
|
input_report_abs(tdev, ABS_TOOL_WIDTH, 1);
|
|
input_report_key(tdev, BTN_TOUCH, 1);
|
|
input_report_key(tdev, BTN_2, 0);
|
|
input_sync(tdev);
|
|
iReportCount++;
|
|
if (iReportCount < 10)
|
|
printk(KERN_INFO "projector: Pen move %d, %d\n", iX, iY);
|
|
}
|
|
}
|
|
} else if (b_prePenDown) {
|
|
input_report_abs(tdev, ABS_X, iX);
|
|
input_report_abs(tdev, ABS_Y, iY);
|
|
input_report_abs(tdev, ABS_PRESSURE, 0);
|
|
input_report_abs(tdev, ABS_TOOL_WIDTH, 0);
|
|
input_report_key(tdev, BTN_TOUCH, 0);
|
|
input_report_key(tdev, BTN_2, 0);
|
|
input_sync(tdev);
|
|
printk(KERN_INFO "projector: Pen up %d, %d\n", iX, iY);
|
|
b_prePenDown = false;
|
|
b_firstPenDown = true;
|
|
iReportCount = 0;
|
|
}
|
|
iCal_LastX = iX;
|
|
iCal_LastY = iY;
|
|
}
|
|
|
|
/* key code: 4 -> home, 5-> menu, 6 -> back, 0 -> system wake */
|
|
static void projector_send_Key_event(struct projector_dev *ctxt,
|
|
int iKeycode)
|
|
{
|
|
struct input_dev *kdev = ctxt->keypad_input;
|
|
printk(KERN_INFO "%s keycode %d\n", __func__, iKeycode);
|
|
|
|
input_report_key(kdev, keypad_code[iKeycode], 1);
|
|
input_sync(kdev);
|
|
input_report_key(kdev, keypad_code[iKeycode], 0);
|
|
input_sync(kdev);
|
|
}
|
|
|
|
static void send_fb(struct projector_dev *ctxt)
|
|
{
|
|
|
|
struct usb_request *req;
|
|
char *frame;
|
|
int xfer;
|
|
int count = ctxt->framesize;
|
|
|
|
if (msmfb_get_fb_area())
|
|
frame = (ctxt->fbaddr + ctxt->framesize);
|
|
else
|
|
frame = ctxt->fbaddr;
|
|
|
|
while (count > 0) {
|
|
req = req_get(ctxt, &ctxt->tx_idle);
|
|
if (req) {
|
|
xfer = count > TXN_MAX? TXN_MAX : count;
|
|
req->length = xfer;
|
|
memcpy(req->buf, frame, xfer);
|
|
if (usb_ep_queue(ctxt->ep_in, req, GFP_ATOMIC) < 0) {
|
|
req_put(ctxt, &ctxt->tx_idle, req);
|
|
printk(KERN_WARNING "%s: failed to queue req %p\n",
|
|
__func__, req);
|
|
break;
|
|
}
|
|
count -= xfer;
|
|
frame += xfer;
|
|
} else {
|
|
DBG("send_fb: no req to send\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
static void send_info(struct projector_dev *ctxt)
|
|
{
|
|
struct usb_request *req;
|
|
|
|
req = req_get(ctxt, &ctxt->tx_idle);
|
|
if (req) {
|
|
req->length = 20;
|
|
memcpy(req->buf, "okay", 4);
|
|
memcpy(req->buf + 4, &ctxt->bitsPixel, 4);
|
|
#if defined(CONFIG_MACH_PARADISE)
|
|
if (machine_is_paradise()) {
|
|
ctxt->framesize = 320 * 480 * 2;
|
|
printk(KERN_INFO "send_info: framesize %d\n",
|
|
ctxt->framesize);
|
|
}
|
|
#endif
|
|
memcpy(req->buf + 8, &ctxt->framesize, 4);
|
|
memcpy(req->buf + 12, &ctxt->width, 4);
|
|
memcpy(req->buf + 16, &ctxt->height, 4);
|
|
if (usb_ep_queue(ctxt->ep_in, req, GFP_ATOMIC) < 0) {
|
|
req_put(ctxt, &ctxt->tx_idle, req);
|
|
printk(KERN_WARNING "%s: failed to queue req %p\n",
|
|
__func__, req);
|
|
}
|
|
} else
|
|
printk(KERN_INFO "%s: no req to send\n", __func__);
|
|
}
|
|
|
|
static void projector_get_msmfb(struct projector_dev *ctxt)
|
|
{
|
|
struct msm_fb_info fb_info;
|
|
|
|
msmfb_get_var(&fb_info);
|
|
|
|
ctxt->bitsPixel = BITSPIXEL;
|
|
ctxt->width = fb_info.xres;
|
|
ctxt->height = fb_info.yres;
|
|
ctxt->fbaddr = fb_info.fb_addr;
|
|
printk(KERN_INFO "projector: width %d, height %d\n",
|
|
fb_info.xres, fb_info.yres);
|
|
|
|
ctxt->framesize = (ctxt->width)*(ctxt->height)*2;
|
|
}
|
|
|
|
static void projector_complete_in(struct usb_ep *ep, struct usb_request *req)
|
|
{
|
|
struct projector_dev *dev = &_projector_dev;
|
|
req_put(dev, &dev->tx_idle, req);
|
|
}
|
|
|
|
static void projector_complete_out(struct usb_ep *ep, struct usb_request *req)
|
|
{
|
|
struct projector_dev *ctxt = &_projector_dev;
|
|
unsigned char *data = req->buf;
|
|
int mouse_data[3];
|
|
int i;
|
|
DBG("%s: status %d, %d bytes\n", __func__,
|
|
req->status, req->actual);
|
|
|
|
if (req->status != 0) {
|
|
ctxt->error = 1;
|
|
req_put(ctxt, &ctxt->rx_idle, req);
|
|
return ;
|
|
}
|
|
|
|
/* for mouse event type, 1 :move, 2:down, 3:up */
|
|
mouse_data[0] = *((int *)(req->buf));
|
|
|
|
if (!strncmp("init", data, 4)) {
|
|
if (!ctxt->init_done) {
|
|
projector_get_msmfb(ctxt);
|
|
ctxt->init_done = 1;
|
|
}
|
|
send_info(ctxt);
|
|
/* system wake code */
|
|
projector_send_Key_event(ctxt, 0);
|
|
} else if (*data == ' ') {
|
|
send_fb(ctxt);
|
|
ctxt->frame_count++;
|
|
/* 30s send system wake code */
|
|
if (ctxt->frame_count == 30 * 30) {
|
|
projector_send_Key_event(ctxt, 0);
|
|
ctxt->frame_count = 0;
|
|
}
|
|
} else if (mouse_data[0] > 0) {
|
|
if (mouse_data[0] < 4) {
|
|
for (i = 0; i < 3; i++)
|
|
mouse_data[i] = *(((int *)(req->buf))+i);
|
|
projector_send_touch_event(ctxt,
|
|
mouse_data[0], mouse_data[1], mouse_data[2]);
|
|
} else {
|
|
projector_send_Key_event(ctxt, mouse_data[0]);
|
|
printk(KERN_INFO "projector: Key command data %02x, keycode %d\n",
|
|
*((char *)(req->buf)), mouse_data[0]);
|
|
}
|
|
} else if (mouse_data[0] != 0)
|
|
printk(KERN_ERR "projector: Unknow command data %02x, mouse %d,%d,%d\n",
|
|
*((char *)(req->buf)), mouse_data[0], mouse_data[1], mouse_data[2]);
|
|
|
|
req_put(ctxt, &ctxt->rx_idle, req);
|
|
projector_queue_out(ctxt);
|
|
}
|
|
|
|
static int __init create_bulk_endpoints(struct projector_dev *dev,
|
|
struct usb_endpoint_descriptor *in_desc,
|
|
struct usb_endpoint_descriptor *out_desc)
|
|
{
|
|
struct usb_composite_dev *cdev = dev->cdev;
|
|
struct usb_request *req;
|
|
struct usb_ep *ep;
|
|
int i;
|
|
|
|
DBG("create_bulk_endpoints dev: %p\n", dev);
|
|
|
|
ep = usb_ep_autoconfig(cdev->gadget, in_desc);
|
|
if (!ep) {
|
|
DBG("usb_ep_autoconfig for ep_in failed\n");
|
|
return -ENODEV;
|
|
}
|
|
DBG("usb_ep_autoconfig for ep_in got %s\n", ep->name);
|
|
ep->driver_data = dev; /* claim the endpoint */
|
|
dev->ep_in = ep;
|
|
|
|
ep = usb_ep_autoconfig(cdev->gadget, out_desc);
|
|
if (!ep) {
|
|
DBG("usb_ep_autoconfig for ep_out failed\n");
|
|
return -ENODEV;
|
|
}
|
|
DBG("usb_ep_autoconfig for projector ep_out got %s\n", ep->name);
|
|
ep->driver_data = dev; /* claim the endpoint */
|
|
dev->ep_out = ep;
|
|
|
|
/* now allocate requests for our endpoints */
|
|
for (i = 0; i < RX_REQ_MAX; i++) {
|
|
req = projector_request_new(dev->ep_out, RXN_MAX);
|
|
if (!req)
|
|
goto fail;
|
|
req->complete = projector_complete_out;
|
|
req_put(dev, &dev->rx_idle, req);
|
|
}
|
|
for (i = 0; i < TX_REQ_MAX; i++) {
|
|
req = projector_request_new(dev->ep_in, TXN_MAX);
|
|
if (!req)
|
|
goto fail;
|
|
req->complete = projector_complete_in;
|
|
req_put(dev, &dev->tx_idle, req);
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
while ((req = req_get(dev, &dev->tx_idle)))
|
|
projector_request_free(req, dev->ep_in);
|
|
while ((req = req_get(dev, &dev->rx_idle)))
|
|
projector_request_free(req, dev->ep_out);
|
|
printk(KERN_ERR "projector: could not allocate requests\n");
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
projector_function_bind(struct usb_configuration *c, struct usb_function *f)
|
|
{
|
|
struct usb_composite_dev *cdev = c->cdev;
|
|
struct projector_dev *dev = func_to_dev(f);
|
|
int id;
|
|
int ret;
|
|
|
|
dev->cdev = cdev;
|
|
DBG("projector_function_bind dev: %p\n", dev);
|
|
|
|
/* allocate interface ID(s) */
|
|
id = usb_interface_id(c, f);
|
|
if (id < 0)
|
|
return id;
|
|
projector_interface_desc.bInterfaceNumber = id;
|
|
|
|
/* allocate endpoints */
|
|
ret = create_bulk_endpoints(dev, &projector_fullspeed_in_desc,
|
|
&projector_fullspeed_out_desc);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* support high speed hardware */
|
|
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
|
projector_highspeed_in_desc.bEndpointAddress =
|
|
projector_fullspeed_in_desc.bEndpointAddress;
|
|
projector_highspeed_out_desc.bEndpointAddress =
|
|
projector_fullspeed_out_desc.bEndpointAddress;
|
|
}
|
|
|
|
DBG("%s speed %s: IN/%s, OUT/%s\n",
|
|
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
|
f->name, dev->ep_in->name, dev->ep_out->name);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
projector_function_unbind(struct usb_configuration *c, struct usb_function *f)
|
|
{
|
|
struct projector_dev *dev = func_to_dev(f);
|
|
struct usb_request *req;
|
|
|
|
spin_lock_irq(&dev->lock);
|
|
|
|
while ((req = req_get(dev, &dev->tx_idle)))
|
|
projector_request_free(req, dev->ep_in);
|
|
while ((req = req_get(dev, &dev->rx_idle)))
|
|
projector_request_free(req, dev->ep_out);
|
|
|
|
dev->online = 0;
|
|
dev->error = 1;
|
|
spin_unlock_irq(&dev->lock);
|
|
}
|
|
|
|
static int projector_function_set_alt(struct usb_function *f,
|
|
unsigned intf, unsigned alt)
|
|
{
|
|
struct projector_dev *dev = func_to_dev(f);
|
|
struct usb_composite_dev *cdev = f->config->cdev;
|
|
int ret;
|
|
|
|
DBG("%s intf: %d alt: %d\n", __func__, intf, alt);
|
|
ret = usb_ep_enable(dev->ep_in,
|
|
ep_choose(cdev->gadget,
|
|
&projector_highspeed_in_desc,
|
|
&projector_fullspeed_in_desc));
|
|
if (ret)
|
|
return ret;
|
|
ret = usb_ep_enable(dev->ep_out,
|
|
ep_choose(cdev->gadget,
|
|
&projector_highspeed_out_desc,
|
|
&projector_fullspeed_out_desc));
|
|
if (ret) {
|
|
usb_ep_disable(dev->ep_in);
|
|
return ret;
|
|
}
|
|
dev->online = 1;
|
|
projector_queue_out(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void projector_function_disable(struct usb_function *f)
|
|
{
|
|
struct projector_dev *dev = func_to_dev(f);
|
|
struct usb_composite_dev *cdev = dev->cdev;
|
|
|
|
DBG("projector_function_disable\n");
|
|
dev->online = 0;
|
|
dev->error = 1;
|
|
usb_ep_disable(dev->ep_in);
|
|
usb_ep_disable(dev->ep_out);
|
|
|
|
VDBG(cdev, "%s disabled\n", dev->function.name);
|
|
}
|
|
|
|
static int projector_touch_init(struct projector_dev *dev)
|
|
{
|
|
int x = dev->width;
|
|
int y = dev->height;
|
|
int ret = 0;
|
|
struct input_dev *tdev = dev->touch_input;
|
|
|
|
dev->touch_input = input_allocate_device();
|
|
if (dev->touch_input == NULL) {
|
|
printk(KERN_ERR "%s: Failed to allocate input device\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
tdev = dev->touch_input;
|
|
tdev->name = "projector_input";
|
|
set_bit(EV_SYN, tdev->evbit);
|
|
set_bit(EV_KEY, tdev->evbit);
|
|
set_bit(BTN_TOUCH, tdev->keybit);
|
|
set_bit(BTN_2, tdev->keybit);
|
|
set_bit(EV_ABS, tdev->evbit);
|
|
|
|
if (x == 0) {
|
|
printk(KERN_ERR "%s: x=0\n", __func__);
|
|
#if defined(CONFIG_ARCH_QSD8X50)
|
|
x = 480;
|
|
#elif defined(CONFIG_MACH_PARADISE)
|
|
if (machine_is_paradise())
|
|
x = 240;
|
|
else
|
|
x = 320;
|
|
#else
|
|
x = 320;
|
|
#endif
|
|
}
|
|
|
|
if (y == 0) {
|
|
printk(KERN_ERR "%s: y=0\n", __func__);
|
|
#if defined(CONFIG_ARCH_QSD8X50)
|
|
y = 800;
|
|
#elif defined(CONFIG_MACH_PARADISE)
|
|
if (machine_is_paradise())
|
|
y = 400;
|
|
else
|
|
y = 480;
|
|
#else
|
|
y = 480;
|
|
#endif
|
|
}
|
|
/* Set input parameters boundary. */
|
|
input_set_abs_params(tdev, ABS_X, 0, x, 0, 0);
|
|
input_set_abs_params(tdev, ABS_Y, 0, y, 0, 0);
|
|
input_set_abs_params(tdev, ABS_PRESSURE, 0, 255, 0, 0);
|
|
input_set_abs_params(tdev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
|
|
input_set_abs_params(tdev, ABS_HAT0X, 0, x, 0, 0);
|
|
input_set_abs_params(tdev, ABS_HAT0Y, 0, y, 0, 0);
|
|
|
|
ret = input_register_device(tdev);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: Unable to register %s input device\n",
|
|
__func__, tdev->name);
|
|
input_free_device(tdev);
|
|
return -1;
|
|
}
|
|
printk(KERN_INFO "%s OK \n", __func__);
|
|
return 0;
|
|
}
|
|
static int projector_keypad_init(struct projector_dev *dev)
|
|
{
|
|
struct input_dev *kdev;
|
|
/* Initialize input device info */
|
|
dev->keypad_input = input_allocate_device();
|
|
if (dev->keypad_input == NULL) {
|
|
printk(KERN_ERR "%s: Failed to allocate input device\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
kdev = dev->keypad_input;
|
|
set_bit(EV_KEY, kdev->evbit);
|
|
set_bit(KEY_HOME, kdev->keybit);
|
|
set_bit(KEY_MENU, kdev->keybit);
|
|
set_bit(KEY_BACK, kdev->keybit);
|
|
set_bit(KEY_WAKEUP, kdev->keybit);
|
|
|
|
kdev->name = "projector-Keypad";
|
|
kdev->phys = "input2";
|
|
kdev->id.bustype = BUS_HOST;
|
|
kdev->id.vendor = 0x0123;
|
|
kdev->id.product = 0x5220 /*dummy value*/;
|
|
kdev->id.version = 0x0100;
|
|
kdev->keycodesize = sizeof(unsigned int);
|
|
|
|
/* Register linux input device */
|
|
if (input_register_device(kdev) < 0) {
|
|
printk(KERN_ERR "%s: Unable to register %s input device\n",
|
|
__func__, kdev->name);
|
|
input_free_device(kdev);
|
|
return -1;
|
|
}
|
|
printk(KERN_INFO "%s OK \n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t store_enable(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int _enabled = simple_strtol(buf, NULL, 0);
|
|
printk(KERN_INFO "projector: %d\n", _enabled);
|
|
android_enable_function(&_projector_dev.function, _enabled);
|
|
_projector_dev.enabled = _enabled;
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_enable(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
buf[0] = '0' + _projector_dev.enabled;
|
|
buf[1] = '\n';
|
|
return 2;
|
|
|
|
}
|
|
static DEVICE_ATTR(enable, 0666, show_enable, store_enable);
|
|
|
|
static void prj_dev_release(struct device *dev) {}
|
|
|
|
static int projector_bind_config(struct usb_configuration *c)
|
|
{
|
|
struct projector_dev *dev = &_projector_dev;
|
|
struct msm_fb_info fb_info;
|
|
int ret = 0;
|
|
|
|
printk(KERN_INFO "projector_bind_config\n");
|
|
ret = usb_string_id(c->cdev);
|
|
if (ret < 0)
|
|
return ret;
|
|
projector_string_defs[0].id = ret;
|
|
projector_interface_desc.iInterface = ret;
|
|
|
|
spin_lock_init(&dev->lock);
|
|
|
|
INIT_LIST_HEAD(&dev->rx_idle);
|
|
INIT_LIST_HEAD(&dev->tx_idle);
|
|
|
|
dev->cdev = c->cdev;
|
|
dev->function.name = "projector";
|
|
dev->function.strings = projector_strings;
|
|
dev->function.descriptors = fs_projector_descs;
|
|
dev->function.hs_descriptors = hs_projector_descs;
|
|
dev->function.bind = projector_function_bind;
|
|
dev->function.unbind = projector_function_unbind;
|
|
dev->function.set_alt = projector_function_set_alt;
|
|
dev->function.disable = projector_function_disable;
|
|
|
|
/* start disabled */
|
|
dev->function.hidden = 1;
|
|
|
|
msmfb_get_var(&fb_info);
|
|
dev->bitsPixel = BITSPIXEL;
|
|
dev->width = fb_info.xres;
|
|
dev->height = fb_info.yres;
|
|
dev->fbaddr = fb_info.fb_addr;
|
|
if (projector_touch_init(dev) < 0)
|
|
goto err;
|
|
if (projector_keypad_init(dev) < 0)
|
|
goto err;
|
|
|
|
if (!dev->pdev)
|
|
goto err;
|
|
prj_dev.release = prj_dev_release;
|
|
prj_dev.parent = &dev->pdev->dev;
|
|
dev_set_name(&prj_dev, "interface");
|
|
|
|
ret = device_register(&prj_dev);
|
|
if (ret != 0) {
|
|
DBG("projector failed to register device: %d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = device_create_file(&prj_dev, &dev_attr_enable);
|
|
if (ret != 0) {
|
|
DBG("projector device_create_file failed: %d\n", ret);
|
|
goto err1;
|
|
}
|
|
|
|
ret = usb_add_function(c, &dev->function);
|
|
if (ret)
|
|
goto err2;
|
|
|
|
return 0;
|
|
err2:
|
|
device_remove_file(&prj_dev, &dev_attr_enable);
|
|
err1:
|
|
device_unregister(&prj_dev);
|
|
err:
|
|
printk(KERN_ERR "projector gadget driver failed to initialize\n");
|
|
return ret;
|
|
}
|
|
|
|
static struct android_usb_function projector_function = {
|
|
.name = "projector",
|
|
.bind_config = projector_bind_config,
|
|
};
|
|
|
|
static int pjr_probe(struct platform_device *pdev)
|
|
{
|
|
struct projector_dev *dev = &_projector_dev;
|
|
dev->pdev = pdev;
|
|
dev->init_done = 0;
|
|
dev->frame_count = 0;
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver pjr_driver = {
|
|
.probe = pjr_probe,
|
|
.driver = { .name = PROJECTOR_FUNCTION_NAME, },
|
|
};
|
|
|
|
static void pjr_release(struct device *dev) {}
|
|
|
|
|
|
static struct platform_device pjr_device = {
|
|
.name = PROJECTOR_FUNCTION_NAME,
|
|
.id = -1,
|
|
.dev = {
|
|
.release = pjr_release,
|
|
},
|
|
};
|
|
static int __init init(void)
|
|
{
|
|
int r;
|
|
printk(KERN_INFO "f_projector init\n");
|
|
r = platform_driver_register(&pjr_driver);
|
|
if (r < 0)
|
|
return r;
|
|
r = platform_device_register(&pjr_device);
|
|
if (r < 0) {
|
|
platform_driver_unregister(&pjr_driver);
|
|
return r;
|
|
}
|
|
android_register_function(&projector_function);
|
|
return 0;
|
|
}
|
|
module_init(init);
|