| /* |
| * HID driver for N-Trig touchscreens |
| * |
| * Copyright (c) 2011 N-TRIG |
| * |
| */ |
| |
| /* |
| * 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. |
| */ |
| |
| #include <linux/device.h> |
| #include <linux/hid.h> |
| #include <linux/module.h> |
| #include <linux/usb.h> |
| |
| #include "typedef-ntrig.h" |
| #include "ntrig-common.h" |
| //#include "hid-ids.h" |
| #include "ntrig-dispatcher.h" |
| /**********************************************************************************/ |
| |
| struct ntrig_usbhid_data; |
| struct ntrig_usbhid_ncp; |
| |
| /** |
| * USB-HID interface API |
| */ |
| static int ntrig_usbhid_input_mapping ( struct hid_device* hdev, |
| struct hid_input* hi, |
| struct hid_field* field, |
| struct hid_usage* usage, |
| unsigned long** bit, |
| int* max); |
| static int ntrig_usbhid_input_mapped ( struct hid_device *hdev, |
| struct hid_input *hi, |
| struct hid_field *field, |
| struct hid_usage *usage, |
| unsigned long **bit, |
| int *max); |
| static int ntrig_usbhid_event ( struct hid_device *hid, |
| struct hid_field *field, |
| struct hid_usage *usage, |
| __s32 value); |
| static int ntrig_usbhid_probe ( struct hid_device *hdev, const struct hid_device_id *id); |
| static void ntrig_usbhid_remove ( struct hid_device *hdev); |
| static int __init ntrig_usbhid_init (void); |
| static void __exit ntrig_usbhid_exit (void); |
| static int ntrig_usbhid_reset_resume( struct hid_device *hdev); |
| |
| |
| /** |
| * Driver Host protocols API |
| */ |
| static int ntrig_usbhid_send_ncp_report ( struct hid_device *hdev, const unsigned char *ncp_cmd); |
| static int ntrig_usbhid_send_report ( struct hid_device *hdev, uint8_t request_report, const char *in_buf, short msg_len); |
| |
| /** |
| * Internal API for struct ntrig_usbhid_data |
| */ |
| void clear_input_buffer (struct ntrig_usbhid_data* nd); |
| void set_button (struct ntrig_usbhid_data* nd, __s32 btn); |
| void process_multi_touch_finger (struct ntrig_usbhid_data* nd); |
| void send_multi_touch (struct ntrig_usbhid_data* nd); |
| void send_pen (struct ntrig_usbhid_data* nd); |
| |
| |
| /**********************************************************************************/ |
| /* |
| * N-trig HID Report Structure |
| * The driver will support MTM firwmare Pen, Fingers |
| */ |
| struct ntrig_usbhid_data { |
| lp_ntrig_bus_device ntrig_dispatcher; |
| mr_message_types_t message; |
| __u16 pressure; |
| __u8 events; |
| __u16 x; |
| __u16 y; |
| __u16 frame_index; |
| __u8 finger_id; |
| __u16 dx; |
| __u16 dy; |
| __u8 generic_byte; |
| __u8 blob_id; |
| __u8 isPalm; |
| __u8 msc_cnt; |
| __s32 btn_pressed; |
| __s32 btn_removed; |
| __u8 first_occurance; |
| __u8 sensor_id; |
| __u8 battery_status; |
| __u8 contact_count; |
| }; |
| |
| void clear_input_buffer(struct ntrig_usbhid_data* nd) |
| { |
| memset(&nd->message, 0, sizeof(nd->message)); |
| } |
| |
| void set_button(struct ntrig_usbhid_data* nd, __s32 btn) |
| { |
| if(nd->btn_pressed != btn){ |
| nd->btn_removed = nd->btn_pressed; |
| nd->btn_pressed = btn; |
| } |
| } |
| |
| void multi_touch_session_end(struct ntrig_usbhid_data* nd) |
| { |
| clear_input_buffer(nd); |
| nd->message.type = MSG_FINGER_PARSE; |
| nd->message.msg.fingers_event.num_of_fingers = 0; |
| nd->message.msg.fingers_event.frame_index = nd->frame_index; |
| nd->message.msg.fingers_event.sensor_id = nd->sensor_id; |
| if (ntrig_write_hid(&nd->message) != DTRG_NO_ERROR) |
| ntrig_dbg("FAILED to end MULTI-TOUCH session\n"); |
| } |
| |
| void process_multi_touch_finger(struct ntrig_usbhid_data* nd) |
| { |
| int index = nd->message.msg.fingers_event.num_of_fingers; |
| |
| if(0 == nd->finger_id){ |
| /* start preparing finger report */ |
| clear_input_buffer(nd); |
| nd->message.type = MSG_FINGER_PARSE; |
| index = 0; |
| } |
| nd->message.msg.fingers_event.finger_array[index].track_id = nd->finger_id; |
| nd->message.msg.fingers_event.finger_array[index].x_coord = nd->x; |
| nd->message.msg.fingers_event.finger_array[index].y_coord = nd->y; |
| nd->message.msg.fingers_event.finger_array[index].dx = nd->dx; |
| nd->message.msg.fingers_event.finger_array[index].dy = nd->dy; |
| /* we pass generic byte via removed field */ |
| nd->message.msg.fingers_event.finger_array[index].removed = nd->generic_byte; |
| nd->message.msg.fingers_event.finger_array[index].palm = nd->isPalm; |
| nd->message.msg.fingers_event.finger_array[index].generic = nd->blob_id; |
| nd->message.msg.fingers_event.num_of_fingers++; |
| } |
| |
| void send_multi_touch(struct ntrig_usbhid_data* nd) |
| { |
| /* lets verify that buffer is used for multi-touch data */ |
| if( MSG_FINGER_PARSE == nd->message.type) { |
| nd->message.msg.fingers_event.num_of_fingers = nd->contact_count; |
| /* add frame index, when sending all fingers data */ |
| nd->message.msg.fingers_event.frame_index = nd->frame_index; |
| nd->message.msg.fingers_event.sensor_id = nd->sensor_id; |
| if (ntrig_write_hid(&nd->message) != DTRG_NO_ERROR) |
| ntrig_dbg("FAILED to send MULTI-TOUCH\n"); |
| } |
| } |
| |
| void send_pen(struct ntrig_usbhid_data* nd) |
| { |
| clear_input_buffer(nd); |
| nd->message.type = MSG_PEN_EVENTS; |
| nd->message.msg.pen_event.x_coord = nd->x; |
| nd->message.msg.pen_event.y_coord = nd->y; |
| nd->message.msg.pen_event.pressure = nd->pressure; |
| nd->message.msg.pen_event.btn_code = nd->btn_pressed; |
| nd->message.msg.pen_event.btn_removed = nd->btn_removed; |
| nd->message.msg.pen_event.sensor_id = nd->sensor_id; |
| nd->message.msg.pen_event.battery_status = nd->battery_status; |
| |
| if (ntrig_write_hid(&nd->message) != DTRG_NO_ERROR) |
| ntrig_dbg("FAILED to send PEN\n"); |
| } |
| /**********************************************************************************/ |
| |
| |
| /* |
| * get feature buffer - as a result of sysfs show and set |
| * first byte indicate amount of byte to copy |
| */ |
| static unsigned char hid_touch_ep_msg[HID_CLASS_TOUCH_EP_LEN]; |
| static ntrig_usbhid_ncp_t ncp; |
| /* |
| * this driver is aimed at two firmware versions in circulation: |
| * - dual pen/finger single touch |
| * - finger multitouch, pen not working |
| */ |
| |
| static int ntrig_usbhid_input_mapping( struct hid_device* hdev, |
| struct hid_input* hi, |
| struct hid_field* field, |
| struct hid_usage* usage, |
| unsigned long** bit, |
| int* max) |
| { |
| struct ntrig_usbhid_data* nd = hid_get_drvdata(hdev); |
| |
| switch (usage->hid & HID_USAGE_PAGE) { |
| case HID_UP_GENDESK: |
| switch (usage->hid) { |
| case HID_GD_X: |
| hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); |
| input_set_abs_params(hi->input, ABS_X, field->logical_minimum, field->logical_maximum, 0, 0); |
| nd->ntrig_dispatcher->logical_min_x = field->logical_minimum; |
| nd->ntrig_dispatcher->logical_max_x = field->logical_maximum; |
| ntrig_dbg("%s ABX_X_MIN=%d ABX_X_MAX=%d\n", __FUNCTION__, field->logical_minimum, field->logical_maximum); |
| return 1; |
| case HID_GD_Y: |
| hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); |
| nd->ntrig_dispatcher->logical_min_y = field->logical_minimum; |
| nd->ntrig_dispatcher->logical_max_y = field->logical_maximum; |
| input_set_abs_params(hi->input, ABS_Y, field->logical_minimum, field->logical_maximum, 0, 0); |
| ntrig_dbg("%s ABX_Y_MIN=%d ABX_Y_MAX=%d\n", __FUNCTION__, field->logical_minimum, field->logical_maximum); |
| return 1; |
| } |
| return 0; |
| |
| case HID_UP_DIGITIZER: |
| switch (usage->hid) { |
| /* we do not want to map these for now */ |
| case HID_DG_INVERT: /* Not support by pen */ |
| case HID_DG_ERASER: /* Not support by pen */ |
| |
| case HID_DG_CONTACTID: /* Not trustworthy, squelch for now */ |
| case HID_DG_INPUTMODE: |
| case HID_DG_DEVICEINDEX: |
| case HID_DG_CONTACTMAX: |
| return -1; |
| /*case HID_DG_TIPSWITCH: |
| hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); |
| return 1;*/ |
| /* width/height mapped on TouchMajor/TouchMinor/Orientation */ |
| case HID_DG_WIDTH: |
| hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MAJOR); |
| return 1; |
| case HID_DG_HEIGHT: |
| hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MINOR); |
| hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TRACKING_ID); |
| return 1; |
| } |
| return 0; |
| |
| case 0xff000000: |
| /* we do not want to map these: no input-oriented meaning */ |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* |
| * This function maps Keys For Pen And Touch events |
| * MSC events used to transfer information about finger status |
| * In curent Frame |
| */ |
| static int ntrig_usbhid_input_mapped( struct hid_device *hdev, |
| struct hid_input *hi, |
| struct hid_field *field, |
| struct hid_usage *usage, |
| unsigned long **bit, |
| int *max) |
| { |
| // ntrig_dbg("%s \n", __FUNCTION__); |
| |
| if (usage->type == EV_KEY || usage->type == EV_ABS) |
| clear_bit(usage->code, *bit); |
| |
| return 0; |
| } |
| |
| /* |
| * This function sends the DRIVER_ALIVE message to the touch screen upon waking |
| * up after a device sleep. |
| */ |
| static int ntrig_usbhid_reset_resume(struct hid_device *hdev) |
| { |
| int ret; |
| ntrig_dbg( "inside %s \n", __FUNCTION__); |
| ret = ntrig_usbhid_send_report(hdev, REPORTID_DRIVER_ALIVE, NULL, 0); |
| if (ret < 0) { |
| dev_err(&hdev->dev, "%s: send REPORTID_DRIVER_ALIVE failed, error code=%d\n", __FUNCTION__, ret); |
| } |
| return 0; |
| } |
| /* |
| * this function is called upon all reports |
| * so that we can filter contact point information, |
| * decide whether we are in multi or single touch mode |
| * and call input_mt_sync after each point if necessary |
| */ |
| static int ntrig_usbhid_event(struct hid_device *hid, struct hid_field *field, |
| struct hid_usage *usage, __s32 value) |
| { |
| struct usb_interface* intf = to_usb_interface(hid->dev.parent); |
| struct usb_host_interface* interface= intf->cur_altsetting; |
| struct usb_endpoint_descriptor* endpoint = &interface->endpoint[0].desc; |
| __s32 btn = BTN_UNPRESSED; |
| static __u16 ncp_report_counter = 0; |
| |
| /* ntrig_dbg("%s: us=%4X, ap=%4X, ep=%4X, cl=%4X val=%4X\n ", |
| __FUNCTION__, usage->hid, field->application, endpoint->bEndpointAddress, hid->claimed, value); */ |
| if ( NCP_EP_ADDRESS == endpoint->bEndpointAddress ) { |
| switch (ncp_report_counter) { |
| case NCP_HEADER: |
| /* We Do Nothing with this, because each report as a diffrent start address */ |
| break; |
| case NCP_HOSTADD_LSB: |
| ncp.host_address = value; |
| break; |
| case NCP_HOSTADD_MSB: |
| ncp.host_address |= value << 8; |
| break; |
| case NCP_MSG_LEN_LSB: |
| ncp.msg_length = value; |
| break; |
| case NCP_MSG_LEN_MSB: |
| ncp.msg_length |= value << 8; |
| break; |
| case NCP_MSG_TYPE: |
| ncp.msg_type = value; |
| case NCP_CODE: |
| ncp.msg_group_code = value; |
| break; |
| case NCP_GROUP: |
| ncp.msg_group_code |= value << 8; |
| break; |
| case NCP_RTN_CODE_1: |
| case NCP_RTN_CODE_2: |
| case NCP_RTN_CODE_3: |
| case NCP_RTN_CODE_4: |
| ncp.return_code[ncp_report_counter - 8] = value; |
| break; |
| case NCP_RESERVED_LSB: |
| ncp.reserved = value; |
| break; |
| case NCP_RESERVED_MSB: |
| ncp.reserved |= value << 8; |
| break; |
| default: |
| ncp.payload[ncp_report_counter -14] = value; |
| break; |
| } |
| if (++ncp_report_counter == field->report_count) |
| ncp_report_counter = 0; |
| return 1; |
| } |
| if (hid->claimed & HID_CLAIMED_INPUT) { |
| struct ntrig_usbhid_data* nd = hid_get_drvdata(hid); |
| struct input_dev* input = field->hidinput->input; |
| |
| /* We attach multi touch to dispatcher if there is no multi touch queue*/ |
| if(!ntrig_check_multi_touch(nd->sensor_id)) { |
| ntrig_dbg("%s Attach Multi Touch device to dispatcher\n", __FUNCTION__); |
| ntrig_attach_multi_touch(nd->sensor_id, input); |
| } |
| |
| /* We attach multi touch to dispatcher if there is no single touch queue */ |
| if(!ntrig_check_single_touch(nd->sensor_id)) { |
| ntrig_dbg("%s Attach Single Touch device to dispatcher\n", __FUNCTION__); |
| ntrig_attach_single_touch(nd->sensor_id, input); |
| } |
| |
| switch (usage->hid) { |
| case HID_GD_X: |
| /* ntrig_dbg("%s: HID_GD_X=%d\n", __FUNCTION__, value); */ |
| nd->x = value; |
| break; |
| case HID_GD_Y: |
| /* ntrig_dbg("%s: HID_GD_Y=%d\n", __FUNCTION__, value); */ |
| nd->y = value; |
| break; |
| } |
| if (field->application == HID_DG_PEN) { |
| switch (usage->hid) { |
| case HID_DG_INRANGE: |
| /* ntrig_dbg("%s: HID_DG_PEN: HID_DG_INRANGE=%x, value %d\n", __FUNCTION__, usage->hid, value); */ |
| nd->events = value; |
| break; |
| case HID_DG_TIPSWITCH: |
| /* ntrig_dbg("%s: HID_DG_PEN: HID_DG_TIPSWITCH=%x, value %d\n", __FUNCTION__, usage->hid, value); */ |
| nd->events |= (value << 1); |
| break; |
| case HID_DG_BARRELSWITCH: |
| /* ntrig_dbg("%s: HID_DG_PEN: HID_DG_BARRELSWITCH=%x, value %d\n", __FUNCTION__, usage->hid, value); */ |
| nd->events |= (value << 2); |
| break; |
| case HID_DG_INVERT: |
| /* ntrig_dbg("%s: HID_DG_PEN: HID_DG_INVERT=%x, value %d\n", __FUNCTION__, usage->hid, value); */ |
| nd->events |= (value << 3); |
| break; |
| case HID_DG_ERASER: |
| /* ntrig_dbg("%s: HID_DG_PEN: HID_DG_ERASER=%x, value %d\n", __FUNCTION__, usage->hid, value); */ |
| nd->events |= (value << 4); |
| break; |
| case HID_DG_TIPPRESSURE: |
| /* ntrig_dbg("%s: HID_DG_PEN: HID_DG_TIPPRESSURE=%x, value %d\n", __FUNCTION__, usage->hid, value); */ |
| nd->pressure = value; |
| btn = (int) nd->events; |
| /* process button information and send to dispatcher if required */ |
| set_button(nd, btn); |
| /* send pen data to dispatcher */ |
| send_pen (nd); |
| /* print button and pen data */ |
| /* ntrig_dbg("Privet X=%d Y=%d Button=%d Pressure=%d\n", |
| nd->x, |
| nd->y, |
| nd->btn_pressed, |
| nd->pressure); */ |
| break; |
| #if 0 |
| case PEN_BATTERY_STATUS: |
| /* ntrig_dbg("%s: HID_DG_PEN: PEN_BATTERY_STATUS=%x, value %d\n", __FUNCTION__, usage->hid, value); */ |
| if (nd->events & 0x01) // TIPSWITCH == true --> pen is touching the screen --> battery_status value is valid |
| nd->battery_status = value; |
| else // TIPSWITCH == false --> pen is not touching the screen --> battery_status value is not valid |
| nd->battery_status = PEN_BUTTON_BATTERY_NOT_AVAILABLE; |
| ntrig_dbg("%s: HID_DG_PEN: PEN_BATTERY_STATUS=%x, value %d, batter_status = %d, \n", __FUNCTION__, usage->hid, value, nd->battery_status); |
| break; |
| #endif |
| default: |
| /* ntrig_dbg("%s: HID_DG_PEN: default=%x, value %d\n", __FUNCTION__, usage->hid, value); */ |
| break; |
| } |
| } else { /* MultiTouch Report */ |
| switch (usage->hid) { |
| case HID_DG_CONTACTCOUNT: |
| nd->contact_count = value; |
| /* end of report (this field always comes last) - send to dispatcher*/ |
| send_multi_touch(nd); |
| break; |
| case MTM_FRAME_INDEX: /* Index 1 */ |
| // ntrig_dbg("%s: MultiTouch Report: MTM_FRAME_INDEX=%x, value %d\n", __FUNCTION__, usage->hid, value); |
| nd->frame_index = value; |
| break; |
| case HID_DG_TIPSWITCH: /* FALLTHRU */ |
| case HID_DG_INRANGE: /* FALLTHRU */ |
| case HID_DG_CONFIDENCE: /* Not Relevant-Index 2 - 4 */ |
| break; |
| case HID_DG_CONTACTID: /* Index 5 */ |
| nd->finger_id = value; |
| break; |
| case HID_DG_WIDTH:/* Index 6 - 7*/ |
| nd->dx = value; |
| break; |
| case HID_DG_HEIGHT:/* Index 8 - 9 */ |
| nd->dy = value; |
| /* Start The Sequence of MSC bytes */ |
| nd->msc_cnt = 0; |
| break; |
| case MTM_PROPRIETARY:/* Index 10 - 14 */ |
| // ntrig_dbg("%s: MultiTouch Report: v=%x, value %d\n", __FUNCTION__, usage->hid, value); |
| nd->msc_cnt++; |
| // ntrig_dbg("%s: MTM_PROPRIETARY msc_cnt=%d val=%d\n", __FUNCTION__, nd->msc_cnt, value); |
| switch (nd->msc_cnt) { |
| case REPORT_GENERIC1: |
| nd->generic_byte = value; |
| break; |
| case REPORT_MT: |
| nd->blob_id = value; |
| break; |
| case REPORT_PALM: |
| nd->isPalm = value; |
| break; |
| case REPORT_GENERIC2: |
| /* end of single finger part of report */ |
| /* process multi touch finger data */ |
| process_multi_touch_finger(nd); |
| /* Print finger data */ |
| ntrig_dbg("Frame=%x Finger=%d X=%d Y=%d DX=%d DY=%d FirstOccur=%d Palm=%d Press=%d Blob=%d\n", |
| nd->frame_index, |
| nd->finger_id, |
| nd->x, |
| nd->y, |
| nd->dx, |
| nd->dy, |
| nd->generic_byte, |
| nd->isPalm, |
| nd->pressure, |
| nd->blob_id); |
| break; |
| } |
| break; |
| } |
| } |
| } |
| /* we have handled the hidinput part, now remains hiddev */ |
| if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event) |
| hid->hiddev_hid_event(hid, field, usage, value); |
| return 1; |
| } |
| |
| /* |
| * This function used to configure N-trig firmware |
| * The first command we need to send to firmware is change |
| * to Multi-touch Mode we don't receive a reply |
| * Endpoint 1 NCP |
| */ |
| static int ntrig_usbhid_send_ncp_report(struct hid_device *hdev, const unsigned char *ncp_cmd) |
| { |
| struct hid_report *report; |
| struct list_head *report_list = |
| &hdev->report_enum[HID_FEATURE_REPORT].report_list; |
| |
| struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
| struct usb_device *dev = interface_to_usbdev(intf); |
| __u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber; |
| int ret; |
| |
| ntrig_dbg( "inside %s \n", __FUNCTION__); |
| ntrig_dbg("%s: ncp_cmd[0]=%0X ncp_cmd[1]=%0X\n", __FUNCTION__, ncp_cmd[0], ncp_cmd[1]); |
| report = list_first_entry(report_list, struct hid_report, list); |
| if (report->maxfield < 1) |
| return -ENODEV; |
| list_for_each_entry(report, |
| report_list, list) { |
| if (report->maxfield < 1) { |
| ntrig_dbg("no fields in the report\n"); |
| continue; |
| } |
| /* |
| * Bytes Needed From User |
| * 0 - Length Of Message |
| * 1 - Reprot ID |
| * .. - Data |
| */ |
| /* ntrig_dbg("%s: checking report id %d for candidate %d\n", __FUNCTION__, report->id, ncp_cmd[1]); */ |
| if (ncp_cmd[HID_NCP_CMD_DATA] == report->id) { |
| char *buf = kmalloc(ncp_cmd[HID_NCP_CMD_LEN], GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| memcpy(buf, &ncp_cmd[HID_NCP_CMD_DATA], ncp_cmd[HID_NCP_CMD_LEN]); |
| ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
| HID_REQ_SET_REPORT , |
| USB_DIR_OUT | USB_TYPE_CLASS | |
| USB_RECIP_INTERFACE, |
| ( 0x03 << 8) | ncp_cmd[HID_NCP_CMD_DATA], ifnum, buf, ncp_cmd[HID_NCP_CMD_LEN], |
| USB_CTRL_SET_TIMEOUT); |
| |
| kfree(buf); |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| * This function is used to configure N-trig firmware |
| * The first command we need to send to firmware is change |
| * to Multi-touch Mode we don't receive a reply |
| * Endpoint 2 - Touch/Pen events |
| */ |
| static int ntrig_usbhid_send_report(struct hid_device *hdev, uint8_t request_report, const char *in_buf, short msg_len) |
| { |
| struct hid_report *report; |
| struct list_head *report_list = |
| &hdev->report_enum[HID_FEATURE_REPORT].report_list; |
| |
| struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
| struct usb_device *dev = interface_to_usbdev(intf); |
| __u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber; |
| int ret = DTRG_FAILED; |
| |
| ntrig_dbg( "inside %s \n", __FUNCTION__); |
| |
| report = list_first_entry(report_list, struct hid_report, list); |
| if (report->maxfield < 1) |
| return -ENODEV; |
| |
| list_for_each_entry(report, |
| report_list, list) { |
| if (report->maxfield < 1) { |
| ntrig_dbg("no fields in the report\n"); |
| continue; |
| } |
| if (request_report == report->id) { |
| int bufsize = (msg_len < HID_CLASS_TOUCH_EP_LEN) ? HID_CLASS_TOUCH_EP_LEN : msg_len; |
| char *buf = kmalloc(bufsize, GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| if (!msg_len) { // msg_len == 0 ==> get report |
| ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), |
| HID_REQ_GET_REPORT, |
| USB_DIR_IN | USB_TYPE_CLASS | |
| USB_RECIP_INTERFACE, |
| (0x03 << 8) | request_report, ifnum, buf, report->size, |
| USB_CTRL_GET_TIMEOUT); |
| hid_touch_ep_msg[HID_REPLY_SIZE] = ret; |
| if (ret >= 0) // success |
| memcpy(&hid_touch_ep_msg[HID_REPLY_DATA], buf, ret); |
| } |
| else { // msg_len > 0 ==> set report |
| if (!in_buf) { |
| kfree(buf); |
| return -ENOMEM; |
| } |
| memcpy(buf, in_buf, msg_len); |
| ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
| HID_REQ_SET_REPORT , |
| USB_DIR_OUT | USB_TYPE_CLASS | |
| USB_RECIP_INTERFACE, |
| (0x03 << 8) | request_report, ifnum, buf, msg_len, |
| USB_CTRL_SET_TIMEOUT); |
| } |
| kfree(buf); |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * Write sensor configuration msg to HID device. |
| * Return number of bytes written/read or <0 if failed. |
| */ |
| static __u8 next_read_sensor_command = -1; |
| |
| int NTRIGWriteSensor (void *dev, uint8_t cmd, const char *in_buf, short msg_len) |
| { |
| int retval; // holds the number of bytes written/read or <0 if failed, so no need for the above check |
| |
| ntrig_dbg( "inside %s cmd = %d\n", __FUNCTION__, cmd); |
| |
| if (!dev) { |
| ntrig_dbg("%s: wrong paramas\n", __FUNCTION__); |
| return DTRG_FAILED; |
| } |
| |
| // Get battery status from SW var, not from device |
| if (cmd == REPORTID_GET_PEN_BATTRY_STATUS) { |
| next_read_sensor_command = REPORTID_GET_PEN_BATTRY_STATUS; |
| // ntrig_dbg("%s: REPORTID_GET_PEN_BATTRY_STATUS: next command = %d \n", __FUNCTION__, next_read_sensor_command); |
| return 1; |
| } |
| |
| retval = ntrig_usbhid_send_report((struct hid_device *)dev, cmd, in_buf, msg_len); |
| |
| ntrig_dbg( "Leaving %s , count = %d, msg_len = %d\n", __FUNCTION__, retval, msg_len); |
| |
| if(retval < 0) { |
| /* failed */ |
| return retval; |
| } |
| |
| return msg_len + 1; // add 1 for command |
| } |
| |
| /** |
| * Read sensor configuration response from HID device. |
| * Return number of bytes read or fail. |
| */ |
| int NTRIGReadSensor (void *dev, char *buf, size_t count) |
| { |
| int size = hid_touch_ep_msg[HID_REPLY_SIZE]; |
| struct ntrig_usbhid_data* nd = hid_get_drvdata((struct hid_device *)dev); |
| |
| ntrig_dbg( "usbhid: inside %s out_buf size = %d \n", __FUNCTION__, (int)sizeof(buf)); |
| // Get battery status from SW var, not from device |
| if (next_read_sensor_command == REPORTID_GET_PEN_BATTRY_STATUS) { |
| buf[0] = (char) nd->battery_status; |
| // ntrig_dbg("%s: REPORTID_GET_PEN_BATTRY_STATUS: status = %d \n", __FUNCTION__, buf[0]); |
| next_read_sensor_command = -1; |
| return 1; |
| } |
| |
| /* size of input buffer (coming from sysfs show function) is PAGE_SIZE (4096 bytes) |
| so it's very unlikely we'll have memory overwriting. |
| Therefore there's no need to check the buf size before writing to it |
| */ |
| if (count < size) { // buffer too small |
| ntrig_dbg( "usbhid: Leaving %s, count (%d)< size(%d)\n", __FUNCTION__, (int)count, size); |
| return -1; |
| } |
| |
| memcpy(buf, &hid_touch_ep_msg[HID_REPLY_DATA], size); |
| ntrig_dbg( "usbhid: Leaving %s \n", __FUNCTION__); |
| |
| return size; |
| } |
| |
| /** |
| * Write NCP msg to HID device. |
| * Return success or fail. |
| */ |
| int NTRIGWriteNCP (void *dev, const char *buf, short msg_len) |
| { |
| int count = 0; |
| struct hid_device *hdev = dev; |
| |
| ntrig_dbg( "inside %s \n", __FUNCTION__); |
| ntrig_dbg("%s: buf[0]=%0X buf[1]=%0X\n", __FUNCTION__, buf[0], buf[1]); |
| |
| if ((!dev) || (!buf) || (msg_len == 0)) { |
| ntrig_dbg("%s: wrong paramas\n", __FUNCTION__); |
| return DTRG_FAILED; |
| } |
| |
| //This version don't support bytes received from shell (ascii) |
| ntrig_usbhid_send_ncp_report(hdev, buf); |
| |
| ntrig_dbg( "Leaving %s , count = %d\n", __FUNCTION__, count); |
| return count; |
| } |
| |
| /** |
| * Read NCP msg from HID device. |
| * Return number of bytes read or fail. |
| */ |
| int NTRIGReadNCP (void *dev, char *buf, size_t count) |
| { |
| int size = sizeof(ntrig_usbhid_ncp_t); |
| |
| ntrig_dbg( "usbhid: inside %s \n", __FUNCTION__); |
| |
| /* allocated buffer too small */ |
| if (count < size) |
| return DTRG_FAILED; |
| |
| /* buf is allocated by dispatcher, therefore no need to call copy_to_user */ |
| memcpy(buf, &ncp, size); |
| return size; |
| } |
| |
| |
| static int ntrig_usbhid_probe(struct hid_device *hdev, const struct hid_device_id *id) |
| { |
| char phys_path[256]; |
| int ret; |
| struct ntrig_usbhid_data* nd; |
| struct usb_interface* intf; |
| struct usb_device* dev; |
| struct usb_host_interface* interface; |
| struct usb_endpoint_descriptor* endpoint; |
| struct _ntrig_dev_ncp_func ncp_func; |
| struct _ntrig_dev_hid_func hid_func; |
| |
| |
| intf = to_usb_interface(hdev->dev.parent); |
| dev = interface_to_usbdev(intf); |
| interface= intf->cur_altsetting; |
| endpoint = &interface->endpoint[0].desc; |
| nd = kmalloc(sizeof(struct ntrig_usbhid_data), GFP_KERNEL); |
| if (!nd) { |
| dev_err(&hdev->dev, "cannot allocate N-Trig data\n"); |
| return -ENOMEM; |
| } |
| if(DTRG_NO_ERROR != ntrig_allocate_device(&nd->ntrig_dispatcher)){ |
| dev_err(&hdev->dev, "cannot allocate N-Trig dispatcher\n"); |
| return DTRG_FAILED; |
| } |
| |
| hid_set_drvdata(hdev, nd); |
| ret = hid_parse(hdev); |
| if (ret) { |
| dev_err(&hdev->dev, "parse failed, error code=%d\n", ret); |
| goto err_free; |
| } |
| |
| if (NCP_EP_ADDRESS == endpoint->bEndpointAddress) { |
| ret = hid_hw_start(hdev, ~HID_QUIRK_MULTI_INPUT); |
| if (ret) { |
| dev_err(&hdev->dev, "hw start failed NCP, error code=%d\n", ret); |
| goto err_free; |
| } |
| |
| /* register device in the dispatcher - set device and function pointers for ncp |
| set sensor id in the device data structure */ |
| /* NOTE: registration of ncp_read, ncp_write & dev is linked. |
| It's the resposibility of this function to set these parameters together */ |
| ncp_func.dev = (void *) hdev; |
| ncp_func.read = NTRIGReadNCP; |
| ncp_func.write = NTRIGWriteNCP; |
| ncp_func.read_counters = NULL;/* counters not implemented in USB driver */ |
| ncp_func.reset_counters = NULL;/* counters not implemented in USB driver */ |
| nd->sensor_id = reg_ntrig_dispatcher(TYPE_BUS_USB_HID, hdev->uniq, &ncp_func, NULL); |
| /* TODO: define behavior in case of fail */ |
| if (nd->sensor_id == DTRG_FAILED) { |
| ntrig_dbg("%s: Cannot register device to dispatcher\n", __FUNCTION__); |
| goto err_free; |
| } |
| ntrig_dbg("%s NCP dev registered with dispatcher, bus_id = %s, sensor_id = %d\n", __FUNCTION__, hdev->uniq, nd->sensor_id); |
| return 0; |
| } |
| |
| else { |
| ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); |
| if (ret) { |
| dev_err(&hdev->dev, "hw start failed TOUCH, error code=%d\n", ret); |
| goto err_free; |
| } |
| ret = ntrig_usbhid_send_report(hdev, REPORTID_DRIVER_ALIVE, NULL, 0); |
| if (ret < 0) { |
| dev_err(&hdev->dev, "send set feature failed, error code=%d\n", ret); |
| goto err_free; |
| } |
| |
| /* register device in the dispatcher - no need to set device and function pointers for touch events |
| set sensor id in the device data structure */ |
| /* NOTE: registration of ncp_read, ncp_write & dev is linked. |
| It's the resposibility of this function to set these parameters together */ |
| hid_func.dev = (void *)hdev; |
| hid_func.read = NTRIGReadSensor; |
| hid_func.write = NTRIGWriteSensor; |
| nd->sensor_id = reg_ntrig_dispatcher(TYPE_BUS_USB_HID, hdev->uniq, NULL, &hid_func); |
| // TODO: define behavior in case of fail |
| if (nd->sensor_id == DTRG_FAILED) { |
| ntrig_dbg("%s: Cannot register device to dispatcher\n", __FUNCTION__); |
| goto err_free; |
| } |
| ntrig_dbg("%s HID dev registered with dispatcher, bus_id = %s, sensor_id = %d\n", __FUNCTION__, hdev->uniq, nd->sensor_id); |
| } |
| /** |
| * Create additional single touch queue |
| */ |
| |
| usb_make_path(dev, phys_path, sizeof(phys_path)); |
| strlcat(phys_path, "/input0", sizeof(phys_path)); |
| |
| nd->ntrig_dispatcher->phys = phys_path; |
| nd->ntrig_dispatcher->name = "USBHID"; |
| nd->ntrig_dispatcher->pressure_min= 0; |
| nd->ntrig_dispatcher->pressure_max= 0xFF; |
| ntrig_create_single_touch(nd->ntrig_dispatcher, nd->sensor_id); |
| ntrig_create_multi_touch (nd->ntrig_dispatcher, nd->sensor_id); |
| |
| nd->battery_status = PEN_BUTTON_BATTERY_NOT_AVAILABLE; |
| |
| ntrig_dbg("Iside %s, bus_id = %d, name = %s, phys = %s, uniq = %s\n", __FUNCTION__, hdev->bus, hdev->name, hdev->phys, hdev->uniq ); |
| |
| ntrig_dbg("End of %s\n", __FUNCTION__); |
| return DTRG_NO_ERROR; |
| err_free: |
| ntrig_dbg("Error End of %s\n", __FUNCTION__); |
| ntrig_remove_device(&nd->ntrig_dispatcher); |
| kfree(nd); |
| return DTRG_FAILED; |
| } |
| |
| static void ntrig_usbhid_remove(struct hid_device *hdev) |
| { |
| struct ntrig_usbhid_data* nd; |
| |
| ntrig_dbg("Entering %s\n", __FUNCTION__); |
| hid_hw_stop(hdev); |
| |
| nd = hid_get_drvdata(hdev); |
| un_reg_ntrig_dispatcher(nd->ntrig_dispatcher, nd->sensor_id, TYPE_BUS_USB_HID, hdev->uniq); |
| ntrig_remove_device(&nd->ntrig_dispatcher); |
| kfree(nd); |
| |
| ntrig_dbg("%s all resource released\n", __FUNCTION__); |
| } |
| |
| static const struct hid_device_id ntrig_devices[] = { |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_3), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_4), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_5), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_6), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_7), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_8), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_9), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_10), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_11), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_12), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_13), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_14), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_15), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18), |
| .driver_data = NTRIG_USB_DEVICE_ID }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(hid, ntrig_devices); |
| |
| static struct hid_driver ntrig_usbhid_driver = { |
| .name = "ntrig_usbhid", |
| .id_table = ntrig_devices, |
| .probe = ntrig_usbhid_probe, |
| .remove = ntrig_usbhid_remove, |
| .input_mapping = ntrig_usbhid_input_mapping, |
| .input_mapped = ntrig_usbhid_input_mapped, |
| .event = ntrig_usbhid_event, |
| .reset_resume = ntrig_usbhid_reset_resume, |
| }; |
| |
| static int __init ntrig_usbhid_init(void) |
| { |
| printk("USBHID Driver Version 2.00\n");//DEBUG Use untill we stablize the version |
| return hid_register_driver(&ntrig_usbhid_driver); |
| } |
| |
| static void __exit ntrig_usbhid_exit(void) |
| { |
| hid_unregister_driver(&ntrig_usbhid_driver); |
| } |
| |
| module_init(ntrig_usbhid_init); |
| module_exit(ntrig_usbhid_exit); |
| |
| MODULE_LICENSE("GPL"); |