| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 1 | /* | 
 | 2 |  * HID Sensors Driver | 
 | 3 |  * Copyright (c) 2012, Intel Corporation. | 
 | 4 |  * | 
 | 5 |  * This program is free software; you can redistribute it and/or modify it | 
 | 6 |  * under the terms and conditions of the GNU General Public License, | 
 | 7 |  * version 2, as published by the Free Software Foundation. | 
 | 8 |  * | 
 | 9 |  * This program is distributed in the hope it will be useful, but WITHOUT | 
 | 10 |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
 | 11 |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
 | 12 |  * more details. | 
 | 13 |  * | 
 | 14 |  * You should have received a copy of the GNU General Public License along with | 
 | 15 |  * this program; if not, write to the Free Software Foundation, Inc., | 
 | 16 |  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 
 | 17 |  * | 
 | 18 |  */ | 
 | 19 | #include <linux/device.h> | 
 | 20 | #include <linux/hid.h> | 
 | 21 | #include <linux/usb.h> | 
 | 22 | #include "usbhid/usbhid.h" | 
 | 23 | #include <linux/module.h> | 
 | 24 | #include <linux/slab.h> | 
 | 25 | #include <linux/mfd/core.h> | 
 | 26 | #include <linux/list.h> | 
 | 27 | #include <linux/hid-sensor-ids.h> | 
 | 28 | #include <linux/hid-sensor-hub.h> | 
 | 29 | #include "hid-ids.h" | 
 | 30 |  | 
 | 31 | /** | 
 | 32 |  * struct sensor_hub_pending - Synchronous read pending information | 
 | 33 |  * @status:		Pending status true/false. | 
 | 34 |  * @ready:		Completion synchronization data. | 
 | 35 |  * @usage_id:		Usage id for physical device, E.g. Gyro usage id. | 
 | 36 |  * @attr_usage_id:	Usage Id of a field, E.g. X-AXIS for a gyro. | 
 | 37 |  * @raw_size:		Response size for a read request. | 
 | 38 |  * @raw_data:		Place holder for received response. | 
 | 39 |  */ | 
 | 40 | struct sensor_hub_pending { | 
 | 41 | 	bool status; | 
 | 42 | 	struct completion ready; | 
 | 43 | 	u32 usage_id; | 
 | 44 | 	u32 attr_usage_id; | 
 | 45 | 	int raw_size; | 
 | 46 | 	u8  *raw_data; | 
 | 47 | }; | 
 | 48 |  | 
 | 49 | /** | 
 | 50 |  * struct sensor_hub_data - Hold a instance data for a HID hub device | 
 | 51 |  * @hsdev:		Stored hid instance for current hub device. | 
 | 52 |  * @mutex:		Mutex to serialize synchronous request. | 
 | 53 |  * @lock:		Spin lock to protect pending request structure. | 
 | 54 |  * @pending:		Holds information of pending sync read request. | 
 | 55 |  * @dyn_callback_list:	Holds callback function | 
 | 56 |  * @dyn_callback_lock:	spin lock to protect callback list | 
 | 57 |  * @hid_sensor_hub_client_devs:	Stores all MFD cells for a hub instance. | 
 | 58 |  * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached). | 
 | 59 |  */ | 
 | 60 | struct sensor_hub_data { | 
 | 61 | 	struct hid_sensor_hub_device *hsdev; | 
 | 62 | 	struct mutex mutex; | 
 | 63 | 	spinlock_t lock; | 
 | 64 | 	struct sensor_hub_pending pending; | 
 | 65 | 	struct list_head dyn_callback_list; | 
 | 66 | 	spinlock_t dyn_callback_lock; | 
 | 67 | 	struct mfd_cell *hid_sensor_hub_client_devs; | 
 | 68 | 	int hid_sensor_client_cnt; | 
 | 69 | }; | 
 | 70 |  | 
 | 71 | /** | 
 | 72 |  * struct hid_sensor_hub_callbacks_list - Stores callback list | 
 | 73 |  * @list:		list head. | 
 | 74 |  * @usage_id:		usage id for a physical device. | 
 | 75 |  * @usage_callback:	Stores registered callback functions. | 
 | 76 |  * @priv:		Private data for a physical device. | 
 | 77 |  */ | 
 | 78 | struct hid_sensor_hub_callbacks_list { | 
 | 79 | 	struct list_head list; | 
 | 80 | 	u32 usage_id; | 
 | 81 | 	struct hid_sensor_hub_callbacks *usage_callback; | 
 | 82 | 	void *priv; | 
 | 83 | }; | 
 | 84 |  | 
 | 85 | static int sensor_hub_check_for_sensor_page(struct hid_device *hdev) | 
 | 86 | { | 
 | 87 | 	int i; | 
 | 88 | 	int ret = -EINVAL; | 
 | 89 |  | 
 | 90 | 	for (i = 0; i < hdev->maxcollection; i++) { | 
 | 91 | 		struct hid_collection *col = &hdev->collection[i]; | 
 | 92 | 		if (col->type == HID_COLLECTION_PHYSICAL && | 
 | 93 | 		   (col->usage & HID_USAGE_PAGE) == HID_UP_SENSOR) { | 
 | 94 | 			ret = 0; | 
 | 95 | 			break; | 
 | 96 | 		} | 
 | 97 | 	} | 
 | 98 |  | 
 | 99 | 	return ret; | 
 | 100 | } | 
 | 101 |  | 
 | 102 | static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev, | 
 | 103 | 						int dir) | 
 | 104 | { | 
 | 105 | 	struct hid_report *report; | 
 | 106 |  | 
 | 107 | 	list_for_each_entry(report, &hdev->report_enum[dir].report_list, list) { | 
 | 108 | 		if (report->id == id) | 
 | 109 | 			return report; | 
 | 110 | 	} | 
 | 111 | 	hid_warn(hdev, "No report with id 0x%x found\n", id); | 
 | 112 |  | 
 | 113 | 	return NULL; | 
 | 114 | } | 
 | 115 |  | 
 | 116 | static int sensor_hub_get_physical_device_count( | 
 | 117 | 				struct hid_report_enum *report_enum) | 
 | 118 | { | 
 | 119 | 	struct hid_report *report; | 
 | 120 | 	struct hid_field *field; | 
 | 121 | 	int cnt = 0; | 
 | 122 |  | 
 | 123 | 	list_for_each_entry(report, &report_enum->report_list, list) { | 
 | 124 | 		field = report->field[0]; | 
 | 125 | 		if (report->maxfield && field && | 
 | 126 | 					field->physical) | 
 | 127 | 			cnt++; | 
 | 128 | 	} | 
 | 129 |  | 
 | 130 | 	return cnt; | 
 | 131 | } | 
 | 132 |  | 
 | 133 | static void sensor_hub_fill_attr_info( | 
 | 134 | 		struct hid_sensor_hub_attribute_info *info, | 
 | 135 | 		s32 index, s32 report_id, s32 units, s32 unit_expo, s32 size) | 
 | 136 | { | 
 | 137 | 	info->index = index; | 
 | 138 | 	info->report_id = report_id; | 
 | 139 | 	info->units = units; | 
 | 140 | 	info->unit_expo = unit_expo; | 
 | 141 | 	info->size = size/8; | 
 | 142 | } | 
 | 143 |  | 
 | 144 | static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( | 
 | 145 | 					struct hid_device *hdev, | 
 | 146 | 					u32 usage_id, void **priv) | 
 | 147 | { | 
 | 148 | 	struct hid_sensor_hub_callbacks_list *callback; | 
 | 149 | 	struct sensor_hub_data *pdata = hid_get_drvdata(hdev); | 
 | 150 |  | 
 | 151 | 	spin_lock(&pdata->dyn_callback_lock); | 
 | 152 | 	list_for_each_entry(callback, &pdata->dyn_callback_list, list) | 
 | 153 | 		if (callback->usage_id == usage_id) { | 
 | 154 | 			*priv = callback->priv; | 
 | 155 | 			spin_unlock(&pdata->dyn_callback_lock); | 
 | 156 | 			return callback->usage_callback; | 
 | 157 | 		} | 
 | 158 | 	spin_unlock(&pdata->dyn_callback_lock); | 
 | 159 |  | 
 | 160 | 	return NULL; | 
 | 161 | } | 
 | 162 |  | 
 | 163 | int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, | 
 | 164 | 			u32 usage_id, | 
 | 165 | 			struct hid_sensor_hub_callbacks *usage_callback) | 
 | 166 | { | 
 | 167 | 	struct hid_sensor_hub_callbacks_list *callback; | 
 | 168 | 	struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); | 
 | 169 |  | 
 | 170 | 	spin_lock(&pdata->dyn_callback_lock); | 
 | 171 | 	list_for_each_entry(callback, &pdata->dyn_callback_list, list) | 
 | 172 | 		if (callback->usage_id == usage_id) { | 
 | 173 | 			spin_unlock(&pdata->dyn_callback_lock); | 
 | 174 | 			return -EINVAL; | 
 | 175 | 		} | 
| Dan Carpenter | 2b7c4b8 | 2012-09-14 06:53:23 +0000 | [diff] [blame] | 176 | 	callback = kzalloc(sizeof(*callback), GFP_ATOMIC); | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 177 | 	if (!callback) { | 
 | 178 | 		spin_unlock(&pdata->dyn_callback_lock); | 
 | 179 | 		return -ENOMEM; | 
 | 180 | 	} | 
 | 181 | 	callback->usage_callback = usage_callback; | 
 | 182 | 	callback->usage_id = usage_id; | 
 | 183 | 	callback->priv = NULL; | 
 | 184 | 	list_add_tail(&callback->list, &pdata->dyn_callback_list); | 
 | 185 | 	spin_unlock(&pdata->dyn_callback_lock); | 
 | 186 |  | 
 | 187 | 	return 0; | 
 | 188 | } | 
 | 189 | EXPORT_SYMBOL_GPL(sensor_hub_register_callback); | 
 | 190 |  | 
 | 191 | int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, | 
 | 192 | 				u32 usage_id) | 
 | 193 | { | 
 | 194 | 	struct hid_sensor_hub_callbacks_list *callback; | 
 | 195 | 	struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); | 
 | 196 |  | 
 | 197 | 	spin_lock(&pdata->dyn_callback_lock); | 
 | 198 | 	list_for_each_entry(callback, &pdata->dyn_callback_list, list) | 
 | 199 | 		if (callback->usage_id == usage_id) { | 
 | 200 | 			list_del(&callback->list); | 
 | 201 | 			kfree(callback); | 
 | 202 | 			break; | 
 | 203 | 		} | 
 | 204 | 	spin_unlock(&pdata->dyn_callback_lock); | 
 | 205 |  | 
 | 206 | 	return 0; | 
 | 207 | } | 
 | 208 | EXPORT_SYMBOL_GPL(sensor_hub_remove_callback); | 
 | 209 |  | 
 | 210 | int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, | 
 | 211 | 				u32 field_index, s32 value) | 
 | 212 | { | 
 | 213 | 	struct hid_report *report; | 
 | 214 | 	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev); | 
 | 215 | 	int ret = 0; | 
 | 216 |  | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 217 | 	mutex_lock(&data->mutex); | 
 | 218 | 	report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); | 
 | 219 | 	if (!report || (field_index >=  report->maxfield)) { | 
 | 220 | 		ret = -EINVAL; | 
 | 221 | 		goto done_proc; | 
 | 222 | 	} | 
 | 223 | 	hid_set_field(report->field[field_index], 0, value); | 
 | 224 | 	usbhid_submit_report(hsdev->hdev, report, USB_DIR_OUT); | 
 | 225 | 	usbhid_wait_io(hsdev->hdev); | 
 | 226 |  | 
 | 227 | done_proc: | 
 | 228 | 	mutex_unlock(&data->mutex); | 
 | 229 |  | 
 | 230 | 	return ret; | 
 | 231 | } | 
 | 232 | EXPORT_SYMBOL_GPL(sensor_hub_set_feature); | 
 | 233 |  | 
 | 234 | int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, | 
 | 235 | 				u32 field_index, s32 *value) | 
 | 236 | { | 
 | 237 | 	struct hid_report *report; | 
 | 238 | 	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev); | 
 | 239 | 	int ret = 0; | 
 | 240 |  | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 241 | 	mutex_lock(&data->mutex); | 
 | 242 | 	report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); | 
 | 243 | 	if (!report || (field_index >=  report->maxfield)) { | 
 | 244 | 		ret = -EINVAL; | 
 | 245 | 		goto done_proc; | 
 | 246 | 	} | 
 | 247 | 	usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); | 
 | 248 | 	usbhid_wait_io(hsdev->hdev); | 
 | 249 | 	*value = report->field[field_index]->value[0]; | 
 | 250 |  | 
 | 251 | done_proc: | 
 | 252 | 	mutex_unlock(&data->mutex); | 
 | 253 |  | 
 | 254 | 	return ret; | 
 | 255 | } | 
 | 256 | EXPORT_SYMBOL_GPL(sensor_hub_get_feature); | 
 | 257 |  | 
 | 258 |  | 
 | 259 | int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, | 
 | 260 | 					u32 usage_id, | 
 | 261 | 					u32 attr_usage_id, u32 report_id) | 
 | 262 | { | 
 | 263 | 	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev); | 
 | 264 | 	unsigned long flags; | 
 | 265 | 	struct hid_report *report; | 
 | 266 | 	int ret_val = 0; | 
 | 267 |  | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 268 | 	mutex_lock(&data->mutex); | 
 | 269 | 	memset(&data->pending, 0, sizeof(data->pending)); | 
 | 270 | 	init_completion(&data->pending.ready); | 
 | 271 | 	data->pending.usage_id = usage_id; | 
 | 272 | 	data->pending.attr_usage_id = attr_usage_id; | 
 | 273 | 	data->pending.raw_size = 0; | 
 | 274 |  | 
 | 275 | 	spin_lock_irqsave(&data->lock, flags); | 
 | 276 | 	data->pending.status = true; | 
 | 277 | 	report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); | 
 | 278 | 	if (!report) { | 
 | 279 | 		spin_unlock_irqrestore(&data->lock, flags); | 
 | 280 | 		goto err_free; | 
 | 281 | 	} | 
 | 282 | 	usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); | 
 | 283 | 	spin_unlock_irqrestore(&data->lock, flags); | 
 | 284 | 	wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5); | 
 | 285 | 	switch (data->pending.raw_size) { | 
 | 286 | 	case 1: | 
 | 287 | 		ret_val = *(u8 *)data->pending.raw_data; | 
 | 288 | 		break; | 
 | 289 | 	case 2: | 
 | 290 | 		ret_val = *(u16 *)data->pending.raw_data; | 
 | 291 | 		break; | 
 | 292 | 	case 4: | 
 | 293 | 		ret_val = *(u32 *)data->pending.raw_data; | 
 | 294 | 		break; | 
 | 295 | 	default: | 
 | 296 | 		ret_val = 0; | 
 | 297 | 	} | 
 | 298 | 	kfree(data->pending.raw_data); | 
 | 299 |  | 
 | 300 | err_free: | 
 | 301 | 	data->pending.status = false; | 
 | 302 | 	mutex_unlock(&data->mutex); | 
 | 303 |  | 
 | 304 | 	return ret_val; | 
 | 305 | } | 
 | 306 | EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value); | 
 | 307 |  | 
 | 308 | int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, | 
 | 309 | 				u8 type, | 
 | 310 | 				u32 usage_id, | 
 | 311 | 				u32 attr_usage_id, | 
 | 312 | 				struct hid_sensor_hub_attribute_info *info) | 
 | 313 | { | 
 | 314 | 	int ret = -1; | 
 | 315 | 	int i, j; | 
 | 316 | 	int collection_index = -1; | 
 | 317 | 	struct hid_report *report; | 
 | 318 | 	struct hid_field *field; | 
 | 319 | 	struct hid_report_enum *report_enum; | 
 | 320 | 	struct hid_device *hdev = hsdev->hdev; | 
 | 321 |  | 
 | 322 | 	/* Initialize with defaults */ | 
 | 323 | 	info->usage_id = usage_id; | 
 | 324 | 	info->attrib_id =  attr_usage_id; | 
 | 325 | 	info->report_id = -1; | 
 | 326 | 	info->index = -1; | 
 | 327 | 	info->units = -1; | 
 | 328 | 	info->unit_expo = -1; | 
 | 329 |  | 
 | 330 | 	for (i = 0; i < hdev->maxcollection; ++i) { | 
 | 331 | 		struct hid_collection *collection = &hdev->collection[i]; | 
 | 332 | 		if (usage_id == collection->usage) { | 
 | 333 | 			collection_index = i; | 
 | 334 | 			break; | 
 | 335 | 		} | 
 | 336 | 	} | 
 | 337 | 	if (collection_index == -1) | 
 | 338 | 		goto err_ret; | 
 | 339 |  | 
 | 340 | 	report_enum = &hdev->report_enum[type]; | 
 | 341 | 	list_for_each_entry(report, &report_enum->report_list, list) { | 
 | 342 | 		for (i = 0; i < report->maxfield; ++i) { | 
 | 343 | 			field = report->field[i]; | 
 | 344 | 			if (field->physical == usage_id && | 
 | 345 | 				field->logical == attr_usage_id) { | 
 | 346 | 				sensor_hub_fill_attr_info(info, i, report->id, | 
 | 347 | 					field->unit, field->unit_exponent, | 
 | 348 | 					field->report_size); | 
 | 349 | 				ret = 0; | 
 | 350 | 			} else { | 
 | 351 | 				for (j = 0; j < field->maxusage; ++j) { | 
 | 352 | 					if (field->usage[j].hid == | 
 | 353 | 					attr_usage_id && | 
 | 354 | 					field->usage[j].collection_index == | 
 | 355 | 					collection_index)  { | 
 | 356 | 						sensor_hub_fill_attr_info(info, | 
 | 357 | 							i, report->id, | 
 | 358 | 							field->unit, | 
 | 359 | 							field->unit_exponent, | 
 | 360 | 							field->report_size); | 
 | 361 | 						ret = 0; | 
 | 362 | 						break; | 
 | 363 | 					} | 
 | 364 | 				} | 
 | 365 | 			} | 
 | 366 | 			if (ret == 0) | 
 | 367 | 				break; | 
 | 368 | 		} | 
 | 369 | 	} | 
 | 370 |  | 
 | 371 | err_ret: | 
 | 372 | 	return ret; | 
 | 373 | } | 
 | 374 | EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info); | 
 | 375 |  | 
 | 376 | #ifdef CONFIG_PM | 
 | 377 | static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message) | 
 | 378 | { | 
 | 379 | 	struct sensor_hub_data *pdata =  hid_get_drvdata(hdev); | 
 | 380 | 	struct hid_sensor_hub_callbacks_list *callback; | 
 | 381 |  | 
 | 382 | 	hid_dbg(hdev, " sensor_hub_suspend\n"); | 
 | 383 | 	spin_lock(&pdata->dyn_callback_lock); | 
 | 384 | 	list_for_each_entry(callback, &pdata->dyn_callback_list, list) { | 
 | 385 | 		if (callback->usage_callback->suspend) | 
 | 386 | 			callback->usage_callback->suspend( | 
 | 387 | 					pdata->hsdev, callback->priv); | 
 | 388 | 	} | 
 | 389 | 	spin_unlock(&pdata->dyn_callback_lock); | 
 | 390 |  | 
 | 391 | 	return 0; | 
 | 392 | } | 
 | 393 |  | 
 | 394 | static int sensor_hub_resume(struct hid_device *hdev) | 
 | 395 | { | 
 | 396 | 	struct sensor_hub_data *pdata =  hid_get_drvdata(hdev); | 
 | 397 | 	struct hid_sensor_hub_callbacks_list *callback; | 
 | 398 |  | 
 | 399 | 	hid_dbg(hdev, " sensor_hub_resume\n"); | 
 | 400 | 	spin_lock(&pdata->dyn_callback_lock); | 
 | 401 | 	list_for_each_entry(callback, &pdata->dyn_callback_list, list) { | 
 | 402 | 		if (callback->usage_callback->resume) | 
 | 403 | 			callback->usage_callback->resume( | 
 | 404 | 					pdata->hsdev, callback->priv); | 
 | 405 | 	} | 
 | 406 | 	spin_unlock(&pdata->dyn_callback_lock); | 
 | 407 |  | 
 | 408 | 	return 0; | 
 | 409 | } | 
 | 410 |  | 
 | 411 | static int sensor_hub_reset_resume(struct hid_device *hdev) | 
 | 412 | { | 
 | 413 | 	return 0; | 
 | 414 | } | 
 | 415 | #endif | 
 | 416 | /* | 
 | 417 |  * Handle raw report as sent by device | 
 | 418 |  */ | 
 | 419 | static int sensor_hub_raw_event(struct hid_device *hdev, | 
 | 420 | 		struct hid_report *report, u8 *raw_data, int size) | 
 | 421 | { | 
 | 422 | 	int i; | 
 | 423 | 	u8 *ptr; | 
 | 424 | 	int sz; | 
 | 425 | 	struct sensor_hub_data *pdata = hid_get_drvdata(hdev); | 
 | 426 | 	unsigned long flags; | 
 | 427 | 	struct hid_sensor_hub_callbacks *callback = NULL; | 
 | 428 | 	struct hid_collection *collection = NULL; | 
 | 429 | 	void *priv = NULL; | 
 | 430 |  | 
 | 431 | 	hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", | 
 | 432 | 			 report->id, size, report->type); | 
 | 433 | 	hid_dbg(hdev, "maxfield:%d\n", report->maxfield); | 
 | 434 | 	if (report->type != HID_INPUT_REPORT) | 
 | 435 | 		return 1; | 
 | 436 |  | 
 | 437 | 	ptr = raw_data; | 
 | 438 | 	ptr++; /*Skip report id*/ | 
 | 439 |  | 
 | 440 | 	if (!report) | 
 | 441 | 		goto err_report; | 
 | 442 |  | 
 | 443 | 	spin_lock_irqsave(&pdata->lock, flags); | 
 | 444 |  | 
 | 445 | 	for (i = 0; i < report->maxfield; ++i) { | 
 | 446 |  | 
 | 447 | 		hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n", | 
 | 448 | 				i, report->field[i]->usage->collection_index, | 
 | 449 | 				report->field[i]->usage->hid, | 
 | 450 | 				report->field[i]->report_size/8); | 
 | 451 |  | 
 | 452 | 		sz = report->field[i]->report_size/8; | 
 | 453 | 		if (pdata->pending.status && pdata->pending.attr_usage_id == | 
 | 454 | 				report->field[i]->usage->hid) { | 
 | 455 | 			hid_dbg(hdev, "data was pending ...\n"); | 
| Dan Carpenter | 2b7c4b8 | 2012-09-14 06:53:23 +0000 | [diff] [blame] | 456 | 			pdata->pending.raw_data = kmalloc(sz, GFP_ATOMIC); | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 457 | 			if (pdata->pending.raw_data) { | 
 | 458 | 				memcpy(pdata->pending.raw_data, ptr, sz); | 
 | 459 | 				pdata->pending.raw_size  = sz; | 
 | 460 | 			} else | 
 | 461 | 				pdata->pending.raw_size = 0; | 
 | 462 | 			complete(&pdata->pending.ready); | 
 | 463 | 		} | 
 | 464 | 		collection = &hdev->collection[ | 
 | 465 | 				report->field[i]->usage->collection_index]; | 
 | 466 | 		hid_dbg(hdev, "collection->usage %x\n", | 
 | 467 | 					collection->usage); | 
 | 468 | 		callback = sensor_hub_get_callback(pdata->hsdev->hdev, | 
 | 469 | 						report->field[i]->physical, | 
 | 470 | 							&priv); | 
 | 471 | 		if (callback && callback->capture_sample) { | 
 | 472 | 			if (report->field[i]->logical) | 
 | 473 | 				callback->capture_sample(pdata->hsdev, | 
 | 474 | 					report->field[i]->logical, sz, ptr, | 
 | 475 | 					callback->pdev); | 
 | 476 | 			else | 
 | 477 | 				callback->capture_sample(pdata->hsdev, | 
 | 478 | 					report->field[i]->usage->hid, sz, ptr, | 
 | 479 | 					callback->pdev); | 
 | 480 | 		} | 
 | 481 | 		ptr += sz; | 
 | 482 | 	} | 
 | 483 | 	if (callback && collection && callback->send_event) | 
 | 484 | 		callback->send_event(pdata->hsdev, collection->usage, | 
 | 485 | 				callback->pdev); | 
 | 486 | 	spin_unlock_irqrestore(&pdata->lock, flags); | 
 | 487 |  | 
 | 488 | err_report: | 
 | 489 | 	return 1; | 
 | 490 | } | 
 | 491 |  | 
 | 492 | static int sensor_hub_probe(struct hid_device *hdev, | 
 | 493 | 				const struct hid_device_id *id) | 
 | 494 | { | 
 | 495 | 	int ret; | 
 | 496 | 	struct sensor_hub_data *sd; | 
 | 497 | 	int i; | 
 | 498 | 	char *name; | 
 | 499 | 	struct hid_report *report; | 
 | 500 | 	struct hid_report_enum *report_enum; | 
 | 501 | 	struct hid_field *field; | 
 | 502 | 	int dev_cnt; | 
 | 503 |  | 
 | 504 | 	sd = kzalloc(sizeof(struct sensor_hub_data), GFP_KERNEL); | 
 | 505 | 	if (!sd) { | 
 | 506 | 		hid_err(hdev, "cannot allocate Sensor data\n"); | 
 | 507 | 		return -ENOMEM; | 
 | 508 | 	} | 
 | 509 | 	sd->hsdev = kzalloc(sizeof(struct hid_sensor_hub_device), GFP_KERNEL); | 
 | 510 | 	if (!sd->hsdev) { | 
 | 511 | 		hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); | 
 | 512 | 		ret = -ENOMEM; | 
 | 513 | 		goto err_free_hub; | 
 | 514 | 	} | 
 | 515 | 	hid_set_drvdata(hdev, sd); | 
 | 516 | 	sd->hsdev->hdev = hdev; | 
 | 517 | 	sd->hsdev->vendor_id = hdev->vendor; | 
 | 518 | 	sd->hsdev->product_id = hdev->product; | 
 | 519 | 	spin_lock_init(&sd->lock); | 
 | 520 | 	spin_lock_init(&sd->dyn_callback_lock); | 
 | 521 | 	mutex_init(&sd->mutex); | 
 | 522 | 	ret = hid_parse(hdev); | 
 | 523 | 	if (ret) { | 
 | 524 | 		hid_err(hdev, "parse failed\n"); | 
 | 525 | 		goto err_free; | 
 | 526 | 	} | 
 | 527 | 	if (sensor_hub_check_for_sensor_page(hdev) < 0) { | 
 | 528 | 		hid_err(hdev, "sensor page not found\n"); | 
 | 529 | 		goto err_free; | 
 | 530 | 	} | 
 | 531 | 	INIT_LIST_HEAD(&hdev->inputs); | 
 | 532 |  | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 533 | 	ret = hid_hw_start(hdev, 0); | 
 | 534 | 	if (ret) { | 
 | 535 | 		hid_err(hdev, "hw start failed\n"); | 
 | 536 | 		goto err_free; | 
 | 537 | 	} | 
 | 538 | 	ret = hid_hw_open(hdev); | 
 | 539 | 	if (ret) { | 
 | 540 | 		hid_err(hdev, "failed to open input interrupt pipe\n"); | 
 | 541 | 		goto err_stop_hw; | 
 | 542 | 	} | 
 | 543 |  | 
 | 544 | 	INIT_LIST_HEAD(&sd->dyn_callback_list); | 
 | 545 | 	sd->hid_sensor_client_cnt = 0; | 
 | 546 | 	report_enum = &hdev->report_enum[HID_INPUT_REPORT]; | 
 | 547 |  | 
 | 548 | 	dev_cnt = sensor_hub_get_physical_device_count(report_enum); | 
 | 549 | 	if (dev_cnt > HID_MAX_PHY_DEVICES) { | 
 | 550 | 		hid_err(hdev, "Invalid Physical device count\n"); | 
 | 551 | 		ret = -EINVAL; | 
 | 552 | 		goto err_close; | 
 | 553 | 	} | 
 | 554 | 	sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt * | 
 | 555 | 						sizeof(struct mfd_cell), | 
 | 556 | 						GFP_KERNEL); | 
 | 557 | 	if (sd->hid_sensor_hub_client_devs == NULL) { | 
| Axel Lin | f2f13a6 | 2012-09-19 16:30:00 +0100 | [diff] [blame] | 558 | 		hid_err(hdev, "Failed to allocate memory for mfd cells\n"); | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 559 | 			ret = -ENOMEM; | 
 | 560 | 			goto err_close; | 
 | 561 | 	} | 
 | 562 | 	list_for_each_entry(report, &report_enum->report_list, list) { | 
 | 563 | 		hid_dbg(hdev, "Report id:%x\n", report->id); | 
 | 564 | 		field = report->field[0]; | 
 | 565 | 		if (report->maxfield && field && | 
 | 566 | 					field->physical) { | 
 | 567 | 			name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x", | 
 | 568 | 						field->physical); | 
 | 569 | 			if (name  == NULL) { | 
| Axel Lin | f2f13a6 | 2012-09-19 16:30:00 +0100 | [diff] [blame] | 570 | 				hid_err(hdev, "Failed MFD device name\n"); | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 571 | 					ret = -ENOMEM; | 
| Axel Lin | f2f13a6 | 2012-09-19 16:30:00 +0100 | [diff] [blame] | 572 | 					goto err_free_names; | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 573 | 			} | 
 | 574 | 			sd->hid_sensor_hub_client_devs[ | 
 | 575 | 				sd->hid_sensor_client_cnt].name = name; | 
 | 576 | 			sd->hid_sensor_hub_client_devs[ | 
 | 577 | 				sd->hid_sensor_client_cnt].platform_data = | 
 | 578 | 						sd->hsdev; | 
 | 579 | 			sd->hid_sensor_hub_client_devs[ | 
 | 580 | 				sd->hid_sensor_client_cnt].pdata_size = | 
 | 581 | 						sizeof(*sd->hsdev); | 
 | 582 | 			hid_dbg(hdev, "Adding %s:%p\n", name, sd); | 
 | 583 | 			sd->hid_sensor_client_cnt++; | 
 | 584 | 		} | 
 | 585 | 	} | 
 | 586 | 	ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs, | 
| Stephen Rothwell | f45c69b | 2012-09-12 07:07:00 +0100 | [diff] [blame] | 587 | 		sd->hid_sensor_client_cnt, NULL, 0, NULL); | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 588 | 	if (ret < 0) | 
 | 589 | 		goto err_free_names; | 
 | 590 |  | 
 | 591 | 	return ret; | 
 | 592 |  | 
 | 593 | err_free_names: | 
 | 594 | 	for (i = 0; i < sd->hid_sensor_client_cnt ; ++i) | 
 | 595 | 		kfree(sd->hid_sensor_hub_client_devs[i].name); | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 596 | 	kfree(sd->hid_sensor_hub_client_devs); | 
 | 597 | err_close: | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 598 | 	hid_hw_close(hdev); | 
 | 599 | err_stop_hw: | 
 | 600 | 	hid_hw_stop(hdev); | 
 | 601 | err_free: | 
 | 602 | 	kfree(sd->hsdev); | 
 | 603 | err_free_hub: | 
 | 604 | 	kfree(sd); | 
 | 605 |  | 
 | 606 | 	return ret; | 
 | 607 | } | 
 | 608 |  | 
 | 609 | static void sensor_hub_remove(struct hid_device *hdev) | 
 | 610 | { | 
 | 611 | 	struct sensor_hub_data *data = hid_get_drvdata(hdev); | 
 | 612 | 	unsigned long flags; | 
 | 613 | 	int i; | 
 | 614 |  | 
 | 615 | 	hid_dbg(hdev, " hardware removed\n"); | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 616 | 	hid_hw_close(hdev); | 
| Axel Lin | f2f13a6 | 2012-09-19 16:30:00 +0100 | [diff] [blame] | 617 | 	hid_hw_stop(hdev); | 
| srinivas pandruvada | 401ca24 | 2012-09-05 13:56:00 +0100 | [diff] [blame] | 618 | 	spin_lock_irqsave(&data->lock, flags); | 
 | 619 | 	if (data->pending.status) | 
 | 620 | 		complete(&data->pending.ready); | 
 | 621 | 	spin_unlock_irqrestore(&data->lock, flags); | 
 | 622 | 	mfd_remove_devices(&hdev->dev); | 
 | 623 | 	for (i = 0; i < data->hid_sensor_client_cnt ; ++i) | 
 | 624 | 		kfree(data->hid_sensor_hub_client_devs[i].name); | 
 | 625 | 	kfree(data->hid_sensor_hub_client_devs); | 
 | 626 | 	hid_set_drvdata(hdev, NULL); | 
 | 627 | 	mutex_destroy(&data->mutex); | 
 | 628 | 	kfree(data->hsdev); | 
 | 629 | 	kfree(data); | 
 | 630 | } | 
 | 631 |  | 
 | 632 | static const struct hid_device_id sensor_hub_devices[] = { | 
 | 633 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, | 
 | 634 | 			USB_DEVICE_ID_SENSOR_HUB_1020) }, | 
 | 635 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, | 
 | 636 | 			USB_DEVICE_ID_SENSOR_HUB_1020) }, | 
 | 637 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, | 
 | 638 | 			USB_DEVICE_ID_SENSOR_HUB_09FA) }, | 
 | 639 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, | 
 | 640 | 			USB_DEVICE_ID_SENSOR_HUB_09FA) }, | 
 | 641 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, | 
 | 642 | 			USB_DEVICE_ID_SENSOR_HUB_7014) }, | 
 | 643 | 	{ } | 
 | 644 | }; | 
 | 645 | MODULE_DEVICE_TABLE(hid, sensor_hub_devices); | 
 | 646 |  | 
 | 647 | static const struct hid_usage_id sensor_hub_grabbed_usages[] = { | 
 | 648 | 	{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, | 
 | 649 | 	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } | 
 | 650 | }; | 
 | 651 |  | 
 | 652 | static struct hid_driver sensor_hub_driver = { | 
 | 653 | 	.name = "hid-sensor-hub", | 
 | 654 | 	.id_table = sensor_hub_devices, | 
 | 655 | 	.probe = sensor_hub_probe, | 
 | 656 | 	.remove = sensor_hub_remove, | 
 | 657 | 	.raw_event = sensor_hub_raw_event, | 
 | 658 | #ifdef CONFIG_PM | 
 | 659 | 	.suspend = sensor_hub_suspend, | 
 | 660 | 	.resume =  sensor_hub_resume, | 
 | 661 | 	.reset_resume =  sensor_hub_reset_resume, | 
 | 662 | #endif | 
 | 663 | }; | 
 | 664 |  | 
 | 665 | static int __init sensor_hub_init(void) | 
 | 666 | { | 
 | 667 | 	return hid_register_driver(&sensor_hub_driver); | 
 | 668 | } | 
 | 669 |  | 
 | 670 | static void __exit sensor_hub_exit(void) | 
 | 671 | { | 
 | 672 | 	hid_unregister_driver(&sensor_hub_driver); | 
 | 673 | } | 
 | 674 |  | 
 | 675 | module_init(sensor_hub_init); | 
 | 676 | module_exit(sensor_hub_exit); | 
 | 677 |  | 
 | 678 | MODULE_DESCRIPTION("HID Sensor Hub driver"); | 
 | 679 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); | 
 | 680 | MODULE_LICENSE("GPL"); |