brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 63c77a3 Raw
521 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * HID class driver for the Greybus.4 *5 * Copyright 2014 Google Inc.6 * Copyright 2014 Linaro Ltd.7 */8 9#include <linux/bitops.h>10#include <linux/hid.h>11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/mutex.h>14#include <linux/slab.h>15#include <linux/greybus.h>16 17/* Greybus HID device's structure */18struct gb_hid {19	struct gb_bundle *bundle;20	struct gb_connection		*connection;21 22	struct hid_device		*hid;23	struct gb_hid_desc_response	hdesc;24 25	unsigned long			flags;26#define GB_HID_STARTED			0x0127#define GB_HID_READ_PENDING		0x0428 29	unsigned int			bufsize;30	char				*inbuf;31};32 33/* Routines to get controller's information over greybus */34 35/* Operations performed on greybus */36static int gb_hid_get_desc(struct gb_hid *ghid)37{38	return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_DESC, NULL,39				 0, &ghid->hdesc, sizeof(ghid->hdesc));40}41 42static int gb_hid_get_report_desc(struct gb_hid *ghid, char *rdesc)43{44	int ret;45 46	ret = gb_pm_runtime_get_sync(ghid->bundle);47	if (ret)48		return ret;49 50	ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT_DESC,51				NULL, 0, rdesc,52				le16_to_cpu(ghid->hdesc.wReportDescLength));53 54	gb_pm_runtime_put_autosuspend(ghid->bundle);55 56	return ret;57}58 59static int gb_hid_set_power(struct gb_hid *ghid, int type)60{61	int ret;62 63	ret = gb_pm_runtime_get_sync(ghid->bundle);64	if (ret)65		return ret;66 67	ret = gb_operation_sync(ghid->connection, type, NULL, 0, NULL, 0);68 69	gb_pm_runtime_put_autosuspend(ghid->bundle);70 71	return ret;72}73 74static int gb_hid_get_report(struct gb_hid *ghid, u8 report_type, u8 report_id,75			     unsigned char *buf, int len)76{77	struct gb_hid_get_report_request request;78	int ret;79 80	ret = gb_pm_runtime_get_sync(ghid->bundle);81	if (ret)82		return ret;83 84	request.report_type = report_type;85	request.report_id = report_id;86 87	ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT,88				&request, sizeof(request), buf, len);89 90	gb_pm_runtime_put_autosuspend(ghid->bundle);91 92	return ret;93}94 95static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id,96			     unsigned char *buf, int len)97{98	struct gb_hid_set_report_request *request;99	struct gb_operation *operation;100	int ret, size = sizeof(*request) + len - 1;101 102	ret = gb_pm_runtime_get_sync(ghid->bundle);103	if (ret)104		return ret;105 106	operation = gb_operation_create(ghid->connection,107					GB_HID_TYPE_SET_REPORT, size, 0,108					GFP_KERNEL);109	if (!operation) {110		gb_pm_runtime_put_autosuspend(ghid->bundle);111		return -ENOMEM;112	}113 114	request = operation->request->payload;115	request->report_type = report_type;116	request->report_id = report_id;117	memcpy(request->report, buf, len);118 119	ret = gb_operation_request_send_sync(operation);120	if (ret) {121		dev_err(&operation->connection->bundle->dev,122			"failed to set report: %d\n", ret);123	} else {124		ret = len;125	}126 127	gb_operation_put(operation);128	gb_pm_runtime_put_autosuspend(ghid->bundle);129 130	return ret;131}132 133static int gb_hid_request_handler(struct gb_operation *op)134{135	struct gb_connection *connection = op->connection;136	struct gb_hid *ghid = gb_connection_get_data(connection);137	struct gb_hid_input_report_request *request = op->request->payload;138 139	if (op->type != GB_HID_TYPE_IRQ_EVENT) {140		dev_err(&connection->bundle->dev,141			"unsupported unsolicited request\n");142		return -EINVAL;143	}144 145	if (test_bit(GB_HID_STARTED, &ghid->flags))146		hid_input_report(ghid->hid, HID_INPUT_REPORT,147				 request->report, op->request->payload_size, 1);148 149	return 0;150}151 152static int gb_hid_report_len(struct hid_report *report)153{154	return ((report->size - 1) >> 3) + 1 +155		report->device->report_enum[report->type].numbered;156}157 158static void gb_hid_find_max_report(struct hid_device *hid, unsigned int type,159				   unsigned int *max)160{161	struct hid_report *report;162	unsigned int size;163 164	list_for_each_entry(report, &hid->report_enum[type].report_list, list) {165		size = gb_hid_report_len(report);166		if (*max < size)167			*max = size;168	}169}170 171static void gb_hid_free_buffers(struct gb_hid *ghid)172{173	kfree(ghid->inbuf);174	ghid->inbuf = NULL;175	ghid->bufsize = 0;176}177 178static int gb_hid_alloc_buffers(struct gb_hid *ghid, size_t bufsize)179{180	ghid->inbuf = kzalloc(bufsize, GFP_KERNEL);181	if (!ghid->inbuf)182		return -ENOMEM;183 184	ghid->bufsize = bufsize;185 186	return 0;187}188 189/* Routines dealing with reports */190static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report)191{192	unsigned int size;193 194	size = gb_hid_report_len(report);195	if (gb_hid_get_report(ghid, report->type, report->id, ghid->inbuf,196			      size))197		return;198 199	/*200	 * hid->driver_lock is held as we are in probe function,201	 * we just need to setup the input fields, so using202	 * hid_report_raw_event is safe.203	 */204	hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1);205}206 207static void gb_hid_init_reports(struct gb_hid *ghid)208{209	struct hid_device *hid = ghid->hid;210	struct hid_report *report;211 212	list_for_each_entry(report,213			    &hid->report_enum[HID_INPUT_REPORT].report_list,214			    list)215		gb_hid_init_report(ghid, report);216 217	list_for_each_entry(report,218			    &hid->report_enum[HID_FEATURE_REPORT].report_list,219			    list)220		gb_hid_init_report(ghid, report);221}222 223static int __gb_hid_get_raw_report(struct hid_device *hid,224				   unsigned char report_number, __u8 *buf, size_t count,225				   unsigned char report_type)226{227	struct gb_hid *ghid = hid->driver_data;228	int ret;229 230	if (report_type == HID_OUTPUT_REPORT)231		return -EINVAL;232 233	ret = gb_hid_get_report(ghid, report_type, report_number, buf, count);234	if (!ret)235		ret = count;236 237	return ret;238}239 240static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf,241				      size_t len, unsigned char report_type)242{243	struct gb_hid *ghid = hid->driver_data;244	int report_id = buf[0];245	int ret;246 247	if (report_type == HID_INPUT_REPORT)248		return -EINVAL;249 250	if (report_id) {251		buf++;252		len--;253	}254 255	ret = gb_hid_set_report(ghid, report_type, report_id, buf, len);256	if (report_id && ret >= 0)257		ret++; /* add report_id to the number of transferred bytes */258 259	return 0;260}261 262static int gb_hid_raw_request(struct hid_device *hid, unsigned char reportnum,263			      __u8 *buf, size_t len, unsigned char rtype,264			      int reqtype)265{266	switch (reqtype) {267	case HID_REQ_GET_REPORT:268		return __gb_hid_get_raw_report(hid, reportnum, buf, len, rtype);269	case HID_REQ_SET_REPORT:270		if (buf[0] != reportnum)271			return -EINVAL;272		return __gb_hid_output_raw_report(hid, buf, len, rtype);273	default:274		return -EIO;275	}276}277 278/* HID Callbacks */279static int gb_hid_parse(struct hid_device *hid)280{281	struct gb_hid *ghid = hid->driver_data;282	unsigned int rsize;283	char *rdesc;284	int ret;285 286	rsize = le16_to_cpu(ghid->hdesc.wReportDescLength);287	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {288		dbg_hid("weird size of report descriptor (%u)\n", rsize);289		return -EINVAL;290	}291 292	rdesc = kzalloc(rsize, GFP_KERNEL);293	if (!rdesc)294		return -ENOMEM;295 296	ret = gb_hid_get_report_desc(ghid, rdesc);297	if (ret) {298		hid_err(hid, "reading report descriptor failed\n");299		goto free_rdesc;300	}301 302	ret = hid_parse_report(hid, rdesc, rsize);303	if (ret)304		dbg_hid("parsing report descriptor failed\n");305 306free_rdesc:307	kfree(rdesc);308 309	return ret;310}311 312static int gb_hid_start(struct hid_device *hid)313{314	struct gb_hid *ghid = hid->driver_data;315	unsigned int bufsize = HID_MIN_BUFFER_SIZE;316	int ret;317 318	gb_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize);319	gb_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize);320	gb_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize);321 322	if (bufsize > HID_MAX_BUFFER_SIZE)323		bufsize = HID_MAX_BUFFER_SIZE;324 325	ret = gb_hid_alloc_buffers(ghid, bufsize);326	if (ret)327		return ret;328 329	if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))330		gb_hid_init_reports(ghid);331 332	return 0;333}334 335static void gb_hid_stop(struct hid_device *hid)336{337	struct gb_hid *ghid = hid->driver_data;338 339	gb_hid_free_buffers(ghid);340}341 342static int gb_hid_open(struct hid_device *hid)343{344	struct gb_hid *ghid = hid->driver_data;345	int ret;346 347	ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON);348	if (ret < 0)349		return ret;350 351	set_bit(GB_HID_STARTED, &ghid->flags);352	return 0;353}354 355static void gb_hid_close(struct hid_device *hid)356{357	struct gb_hid *ghid = hid->driver_data;358	int ret;359 360	clear_bit(GB_HID_STARTED, &ghid->flags);361 362	/* Save some power */363	ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF);364	if (ret)365		dev_err(&ghid->connection->bundle->dev,366			"failed to power off (%d)\n", ret);367}368 369static int gb_hid_power(struct hid_device *hid, int lvl)370{371	struct gb_hid *ghid = hid->driver_data;372 373	switch (lvl) {374	case PM_HINT_FULLON:375		return gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON);376	case PM_HINT_NORMAL:377		return gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF);378	}379 380	return 0;381}382 383/* HID structure to pass callbacks */384static const struct hid_ll_driver gb_hid_ll_driver = {385	.parse = gb_hid_parse,386	.start = gb_hid_start,387	.stop = gb_hid_stop,388	.open = gb_hid_open,389	.close = gb_hid_close,390	.power = gb_hid_power,391	.raw_request = gb_hid_raw_request,392};393 394static int gb_hid_init(struct gb_hid *ghid)395{396	struct hid_device *hid = ghid->hid;397	int ret;398 399	ret = gb_hid_get_desc(ghid);400	if (ret)401		return ret;402 403	hid->version = le16_to_cpu(ghid->hdesc.bcdHID);404	hid->vendor = le16_to_cpu(ghid->hdesc.wVendorID);405	hid->product = le16_to_cpu(ghid->hdesc.wProductID);406	hid->country = ghid->hdesc.bCountryCode;407 408	hid->driver_data = ghid;409	hid->ll_driver = &gb_hid_ll_driver;410	hid->dev.parent = &ghid->connection->bundle->dev;411//	hid->bus = BUS_GREYBUS; /* Need a bustype for GREYBUS in <linux/input.h> */412 413	/* Set HID device's name */414	snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X",415		 dev_name(&ghid->connection->bundle->dev),416		 hid->vendor, hid->product);417 418	return 0;419}420 421static int gb_hid_probe(struct gb_bundle *bundle,422			const struct greybus_bundle_id *id)423{424	struct greybus_descriptor_cport *cport_desc;425	struct gb_connection *connection;426	struct hid_device *hid;427	struct gb_hid *ghid;428	int ret;429 430	if (bundle->num_cports != 1)431		return -ENODEV;432 433	cport_desc = &bundle->cport_desc[0];434	if (cport_desc->protocol_id != GREYBUS_PROTOCOL_HID)435		return -ENODEV;436 437	ghid = kzalloc(sizeof(*ghid), GFP_KERNEL);438	if (!ghid)439		return -ENOMEM;440 441	connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),442					  gb_hid_request_handler);443	if (IS_ERR(connection)) {444		ret = PTR_ERR(connection);445		goto err_free_ghid;446	}447 448	gb_connection_set_data(connection, ghid);449	ghid->connection = connection;450 451	hid = hid_allocate_device();452	if (IS_ERR(hid)) {453		ret = PTR_ERR(hid);454		goto err_connection_destroy;455	}456 457	ghid->hid = hid;458	ghid->bundle = bundle;459 460	greybus_set_drvdata(bundle, ghid);461 462	ret = gb_connection_enable(connection);463	if (ret)464		goto err_destroy_hid;465 466	ret = gb_hid_init(ghid);467	if (ret)468		goto err_connection_disable;469 470	ret = hid_add_device(hid);471	if (ret) {472		hid_err(hid, "can't add hid device: %d\n", ret);473		goto err_connection_disable;474	}475 476	gb_pm_runtime_put_autosuspend(bundle);477 478	return 0;479 480err_connection_disable:481	gb_connection_disable(connection);482err_destroy_hid:483	hid_destroy_device(hid);484err_connection_destroy:485	gb_connection_destroy(connection);486err_free_ghid:487	kfree(ghid);488 489	return ret;490}491 492static void gb_hid_disconnect(struct gb_bundle *bundle)493{494	struct gb_hid *ghid = greybus_get_drvdata(bundle);495 496	if (gb_pm_runtime_get_sync(bundle))497		gb_pm_runtime_get_noresume(bundle);498 499	hid_destroy_device(ghid->hid);500	gb_connection_disable(ghid->connection);501	gb_connection_destroy(ghid->connection);502	kfree(ghid);503}504 505static const struct greybus_bundle_id gb_hid_id_table[] = {506	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_HID) },507	{ }508};509MODULE_DEVICE_TABLE(greybus, gb_hid_id_table);510 511static struct greybus_driver gb_hid_driver = {512	.name		= "hid",513	.probe		= gb_hid_probe,514	.disconnect	= gb_hid_disconnect,515	.id_table	= gb_hid_id_table,516};517module_greybus_driver(gb_hid_driver);518 519MODULE_DESCRIPTION("HID class driver for the Greybus");520MODULE_LICENSE("GPL v2");521