summaryrefslogtreecommitdiff
path: root/include/linux/surface_aggregator/device.h
blob: 46c45d1b63682e34ae523bd632a11dbcff4aff57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Surface System Aggregator Module (SSAM) bus and client-device subsystem.
 *
 * Main interface for the surface-aggregator bus, surface-aggregator client
 * devices, and respective drivers building on top of the SSAM controller.
 * Provides support for non-platform/non-ACPI SSAM clients via dedicated
 * subsystem.
 *
 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
 */

#ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H
#define _LINUX_SURFACE_AGGREGATOR_DEVICE_H

#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/types.h>

#include <linux/surface_aggregator/controller.h>


/* -- Surface System Aggregator Module bus. --------------------------------- */

/**
 * enum ssam_device_domain - SAM device domain.
 * @SSAM_DOMAIN_VIRTUAL:   Virtual device.
 * @SSAM_DOMAIN_SERIALHUB: Physical device connected via Surface Serial Hub.
 */
enum ssam_device_domain {
	SSAM_DOMAIN_VIRTUAL   = 0x00,
	SSAM_DOMAIN_SERIALHUB = 0x01,
};

/**
 * enum ssam_virtual_tc - Target categories for the virtual SAM domain.
 * @SSAM_VIRTUAL_TC_HUB: Device hub category.
 */
enum ssam_virtual_tc {
	SSAM_VIRTUAL_TC_HUB = 0x00,
};

/**
 * struct ssam_device_uid - Unique identifier for SSAM device.
 * @domain:   Domain of the device.
 * @category: Target category of the device.
 * @target:   Target ID of the device.
 * @instance: Instance ID of the device.
 * @function: Sub-function of the device. This field can be used to split a
 *            single SAM device into multiple virtual subdevices to separate
 *            different functionality of that device and allow one driver per
 *            such functionality.
 */
struct ssam_device_uid {
	u8 domain;
	u8 category;
	u8 target;
	u8 instance;
	u8 function;
};

/*
 * Special values for device matching.
 *
 * These values are intended to be used with SSAM_DEVICE(), SSAM_VDEV(), and
 * SSAM_SDEV() exclusively. Specifically, they are used to initialize the
 * match_flags member of the device ID structure. Do not use them directly
 * with struct ssam_device_id or struct ssam_device_uid.
 */
#define SSAM_ANY_TID		0xffff
#define SSAM_ANY_IID		0xffff
#define SSAM_ANY_FUN		0xffff

/**
 * SSAM_DEVICE() - Initialize a &struct ssam_device_id with the given
 * parameters.
 * @d:   Domain of the device.
 * @cat: Target category of the device.
 * @tid: Target ID of the device.
 * @iid: Instance ID of the device.
 * @fun: Sub-function of the device.
 *
 * Initializes a &struct ssam_device_id with the given parameters. See &struct
 * ssam_device_uid for details regarding the parameters. The special values
 * %SSAM_ANY_TID, %SSAM_ANY_IID, and %SSAM_ANY_FUN can be used to specify that
 * matching should ignore target ID, instance ID, and/or sub-function,
 * respectively. This macro initializes the ``match_flags`` field based on the
 * given parameters.
 *
 * Note: The parameters @d and @cat must be valid &u8 values, the parameters
 * @tid, @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID,
 * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not
 * allowed.
 */
#define SSAM_DEVICE(d, cat, tid, iid, fun)					\
	.match_flags = (((tid) != SSAM_ANY_TID) ? SSAM_MATCH_TARGET : 0)	\
		     | (((iid) != SSAM_ANY_IID) ? SSAM_MATCH_INSTANCE : 0)	\
		     | (((fun) != SSAM_ANY_FUN) ? SSAM_MATCH_FUNCTION : 0),	\
	.domain   = d,								\
	.category = cat,							\
	.target   = __builtin_choose_expr((tid) != SSAM_ANY_TID, (tid), 0),	\
	.instance = __builtin_choose_expr((iid) != SSAM_ANY_IID, (iid), 0),	\
	.function = __builtin_choose_expr((fun) != SSAM_ANY_FUN, (fun), 0)

/**
 * SSAM_VDEV() - Initialize a &struct ssam_device_id as virtual device with
 * the given parameters.
 * @cat: Target category of the device.
 * @tid: Target ID of the device.
 * @iid: Instance ID of the device.
 * @fun: Sub-function of the device.
 *
 * Initializes a &struct ssam_device_id with the given parameters in the
 * virtual domain. See &struct ssam_device_uid for details regarding the
 * parameters. The special values %SSAM_ANY_TID, %SSAM_ANY_IID, and
 * %SSAM_ANY_FUN can be used to specify that matching should ignore target ID,
 * instance ID, and/or sub-function, respectively. This macro initializes the
 * ``match_flags`` field based on the given parameters.
 *
 * Note: The parameter @cat must be a valid &u8 value, the parameters @tid,
 * @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID,
 * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not
 * allowed.
 */
#define SSAM_VDEV(cat, tid, iid, fun) \
	SSAM_DEVICE(SSAM_DOMAIN_VIRTUAL, SSAM_VIRTUAL_TC_##cat, tid, iid, fun)

/**
 * SSAM_SDEV() - Initialize a &struct ssam_device_id as physical SSH device
 * with the given parameters.
 * @cat: Target category of the device.
 * @tid: Target ID of the device.
 * @iid: Instance ID of the device.
 * @fun: Sub-function of the device.
 *
 * Initializes a &struct ssam_device_id with the given parameters in the SSH
 * domain. See &struct ssam_device_uid for details regarding the parameters.
 * The special values %SSAM_ANY_TID, %SSAM_ANY_IID, and %SSAM_ANY_FUN can be
 * used to specify that matching should ignore target ID, instance ID, and/or
 * sub-function, respectively. This macro initializes the ``match_flags``
 * field based on the given parameters.
 *
 * Note: The parameter @cat must be a valid &u8 value, the parameters @tid,
 * @iid, and @fun must be either valid &u8 values or %SSAM_ANY_TID,
 * %SSAM_ANY_IID, or %SSAM_ANY_FUN, respectively. Other non-&u8 values are not
 * allowed.
 */
#define SSAM_SDEV(cat, tid, iid, fun) \
	SSAM_DEVICE(SSAM_DOMAIN_SERIALHUB, SSAM_SSH_TC_##cat, tid, iid, fun)

/*
 * enum ssam_device_flags - Flags for SSAM client devices.
 * @SSAM_DEVICE_HOT_REMOVED_BIT:
 *	The device has been hot-removed. Further communication with it may time
 *	out and should be avoided.
 */
enum ssam_device_flags {
	SSAM_DEVICE_HOT_REMOVED_BIT = 0,
};

/**
 * struct ssam_device - SSAM client device.
 * @dev:   Driver model representation of the device.
 * @ctrl:  SSAM controller managing this device.
 * @uid:   UID identifying the device.
 * @flags: Device state flags, see &enum ssam_device_flags.
 */
struct ssam_device {
	struct device dev;
	struct ssam_controller *ctrl;

	struct ssam_device_uid uid;

	unsigned long flags;
};

/**
 * struct ssam_device_driver - SSAM client device driver.
 * @driver:      Base driver model structure.
 * @match_table: Match table specifying which devices the driver should bind to.
 * @probe:       Called when the driver is being bound to a device.
 * @remove:      Called when the driver is being unbound from the device.
 */
struct ssam_device_driver {
	struct device_driver driver;

	const struct ssam_device_id *match_table;

	int  (*probe)(struct ssam_device *sdev);
	void (*remove)(struct ssam_device *sdev);
};

#ifdef CONFIG_SURFACE_AGGREGATOR_BUS

extern struct bus_type ssam_bus_type;
extern const struct device_type ssam_device_type;

/**
 * is_ssam_device() - Check if the given device is a SSAM client device.
 * @d: The device to test the type of.
 *
 * Return: Returns %true if the specified device is of type &struct
 * ssam_device, i.e. the device type points to %ssam_device_type, and %false
 * otherwise.
 */
static inline bool is_ssam_device(struct device *d)
{
	return d->type == &ssam_device_type;
}

#else /* CONFIG_SURFACE_AGGREGATOR_BUS */

static inline bool is_ssam_device(struct device *d)
{
	return false;
}

#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */

/**
 * to_ssam_device() - Casts the given device to a SSAM client device.
 * @d: The device to cast.
 *
 * Casts the given &struct device to a &struct ssam_device. The caller has to
 * ensure that the given device is actually enclosed in a &struct ssam_device,
 * e.g. by calling is_ssam_device().
 *
 * Return: Returns a pointer to the &struct ssam_device wrapping the given
 * device @d.
 */
static inline struct ssam_device *to_ssam_device(struct device *d)
{
	return container_of(d, struct ssam_device, dev);
}

/**
 * to_ssam_device_driver() - Casts the given device driver to a SSAM client
 * device driver.
 * @d: The driver to cast.
 *
 * Casts the given &struct device_driver to a &struct ssam_device_driver. The
 * caller has to ensure that the given driver is actually enclosed in a
 * &struct ssam_device_driver.
 *
 * Return: Returns the pointer to the &struct ssam_device_driver wrapping the
 * given device driver @d.
 */
static inline
struct ssam_device_driver *to_ssam_device_driver(struct device_driver *d)
{
	return container_of(d, struct ssam_device_driver, driver);
}

const struct ssam_device_id *ssam_device_id_match(const struct ssam_device_id *table,
						  const struct ssam_device_uid uid);

const struct ssam_device_id *ssam_device_get_match(const struct ssam_device *dev);

const void *ssam_device_get_match_data(const struct ssam_device *dev);

struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl,
				      struct ssam_device_uid uid);

int ssam_device_add(struct ssam_device *sdev);
void ssam_device_remove(struct ssam_device *sdev);

/**
 * ssam_device_mark_hot_removed() - Mark the given device as hot-removed.
 * @sdev: The device to mark as hot-removed.
 *
 * Mark the device as having been hot-removed. This signals drivers using the
 * device that communication with the device should be avoided and may lead to
 * timeouts.
 */
static inline void ssam_device_mark_hot_removed(struct ssam_device *sdev)
{
	dev_dbg(&sdev->dev, "marking device as hot-removed\n");
	set_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags);
}

/**
 * ssam_device_is_hot_removed() - Check if the given device has been
 * hot-removed.
 * @sdev: The device to check.
 *
 * Checks if the given device has been marked as hot-removed. See
 * ssam_device_mark_hot_removed() for more details.
 *
 * Return: Returns ``true`` if the device has been marked as hot-removed.
 */
static inline bool ssam_device_is_hot_removed(struct ssam_device *sdev)
{
	return test_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags);
}

/**
 * ssam_device_get() - Increment reference count of SSAM client device.
 * @sdev: The device to increment the reference count of.
 *
 * Increments the reference count of the given SSAM client device by
 * incrementing the reference count of the enclosed &struct device via
 * get_device().
 *
 * See ssam_device_put() for the counter-part of this function.
 *
 * Return: Returns the device provided as input.
 */
static inline struct ssam_device *ssam_device_get(struct ssam_device *sdev)
{
	return sdev ? to_ssam_device(get_device(&sdev->dev)) : NULL;
}

/**
 * ssam_device_put() - Decrement reference count of SSAM client device.
 * @sdev: The device to decrement the reference count of.
 *
 * Decrements the reference count of the given SSAM client device by
 * decrementing the reference count of the enclosed &struct device via
 * put_device().
 *
 * See ssam_device_get() for the counter-part of this function.
 */
static inline void ssam_device_put(struct ssam_device *sdev)
{
	if (sdev)
		put_device(&sdev->dev);
}

/**
 * ssam_device_get_drvdata() - Get driver-data of SSAM client device.
 * @sdev: The device to get the driver-data from.
 *
 * Return: Returns the driver-data of the given device, previously set via
 * ssam_device_set_drvdata().
 */
static inline void *ssam_device_get_drvdata(struct ssam_device *sdev)
{
	return dev_get_drvdata(&sdev->dev);
}

/**
 * ssam_device_set_drvdata() - Set driver-data of SSAM client device.
 * @sdev: The device to set the driver-data of.
 * @data: The data to set the device's driver-data pointer to.
 */
static inline void ssam_device_set_drvdata(struct ssam_device *sdev, void *data)
{
	dev_set_drvdata(&sdev->dev, data);
}

int __ssam_device_driver_register(struct ssam_device_driver *d, struct module *o);
void ssam_device_driver_unregister(struct ssam_device_driver *d);

/**
 * ssam_device_driver_register() - Register a SSAM client device driver.
 * @drv: The driver to register.
 */
#define ssam_device_driver_register(drv) \
	__ssam_device_driver_register(drv, THIS_MODULE)

/**
 * module_ssam_device_driver() - Helper macro for SSAM device driver
 * registration.
 * @drv: The driver managed by this module.
 *
 * Helper macro to register a SSAM device driver via module_init() and
 * module_exit(). This macro may only be used once per module and replaces the
 * aforementioned definitions.
 */
#define module_ssam_device_driver(drv)			\
	module_driver(drv, ssam_device_driver_register,	\
		      ssam_device_driver_unregister)


/* -- Helpers for controller and hub devices. ------------------------------- */

#ifdef CONFIG_SURFACE_AGGREGATOR_BUS

int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl,
			    struct fwnode_handle *node);
void ssam_remove_clients(struct device *dev);

#else /* CONFIG_SURFACE_AGGREGATOR_BUS */

static inline int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl,
					  struct fwnode_handle *node)
{
	return 0;
}

static inline void ssam_remove_clients(struct device *dev) {}

#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */

/**
 * ssam_register_clients() - Register all client devices defined under the
 * given parent device.
 * @dev: The parent device under which clients should be registered.
 * @ctrl: The controller with which client should be registered.
 *
 * Register all clients that have via firmware nodes been defined as children
 * of the given (parent) device. The respective child firmware nodes will be
 * associated with the correspondingly created child devices.
 *
 * The given controller will be used to instantiate the new devices. See
 * ssam_device_add() for details.
 *
 * Return: Returns zero on success, nonzero on failure.
 */
static inline int ssam_register_clients(struct device *dev, struct ssam_controller *ctrl)
{
	return __ssam_register_clients(dev, ctrl, dev_fwnode(dev));
}

/**
 * ssam_device_register_clients() - Register all client devices defined under
 * the given SSAM parent device.
 * @sdev: The parent device under which clients should be registered.
 *
 * Register all clients that have via firmware nodes been defined as children
 * of the given (parent) device. The respective child firmware nodes will be
 * associated with the correspondingly created child devices.
 *
 * The controller used by the parent device will be used to instantiate the new
 * devices. See ssam_device_add() for details.
 *
 * Return: Returns zero on success, nonzero on failure.
 */
static inline int ssam_device_register_clients(struct ssam_device *sdev)
{
	return ssam_register_clients(&sdev->dev, sdev->ctrl);
}


/* -- Helpers for client-device requests. ----------------------------------- */

/**
 * SSAM_DEFINE_SYNC_REQUEST_CL_N() - Define synchronous client-device SAM
 * request function with neither argument nor return value.
 * @name: Name of the generated function.
 * @spec: Specification (&struct ssam_request_spec_md) defining the request.
 *
 * Defines a function executing the synchronous SAM request specified by
 * @spec, with the request having neither argument nor return value. Device
 * specifying parameters are not hard-coded, but instead are provided via the
 * client device, specifically its UID, supplied when calling this function.
 * The generated function takes care of setting up the request struct, buffer
 * allocation, as well as execution of the request itself, returning once the
 * request has been fully completed. The required transport buffer will be
 * allocated on the stack.
 *
 * The generated function is defined as ``static int name(struct ssam_device
 * *sdev)``, returning the status of the request, which is zero on success and
 * negative on failure. The ``sdev`` parameter specifies both the target
 * device of the request and by association the controller via which the
 * request is sent.
 *
 * Refer to ssam_request_sync_onstack() for more details on the behavior of
 * the generated function.
 */
#define SSAM_DEFINE_SYNC_REQUEST_CL_N(name, spec...)			\
	SSAM_DEFINE_SYNC_REQUEST_MD_N(__raw_##name, spec)		\
	static int name(struct ssam_device *sdev)			\
	{								\
		return __raw_##name(sdev->ctrl, sdev->uid.target,	\
				    sdev->uid.instance);		\
	}

/**
 * SSAM_DEFINE_SYNC_REQUEST_CL_W() - Define synchronous client-device SAM
 * request function with argument.
 * @name:  Name of the generated function.
 * @atype: Type of the request's argument.
 * @spec:  Specification (&struct ssam_request_spec_md) defining the request.
 *
 * Defines a function executing the synchronous SAM request specified by
 * @spec, with the request taking an argument of type @atype and having no
 * return value. Device specifying parameters are not hard-coded, but instead
 * are provided via the client device, specifically its UID, supplied when
 * calling this function. The generated function takes care of setting up the
 * request struct, buffer allocation, as well as execution of the request
 * itself, returning once the request has been fully completed. The required
 * transport buffer will be allocated on the stack.
 *
 * The generated function is defined as ``static int name(struct ssam_device
 * *sdev, const atype *arg)``, returning the status of the request, which is
 * zero on success and negative on failure. The ``sdev`` parameter specifies
 * both the target device of the request and by association the controller via
 * which the request is sent. The request's argument is specified via the
 * ``arg`` pointer.
 *
 * Refer to ssam_request_sync_onstack() for more details on the behavior of
 * the generated function.
 */
#define SSAM_DEFINE_SYNC_REQUEST_CL_W(name, atype, spec...)		\
	SSAM_DEFINE_SYNC_REQUEST_MD_W(__raw_##name, atype, spec)	\
	static int name(struct ssam_device *sdev, const atype *arg)	\
	{								\
		return __raw_##name(sdev->ctrl, sdev->uid.target,	\
				    sdev->uid.instance, arg);		\
	}

/**
 * SSAM_DEFINE_SYNC_REQUEST_CL_R() - Define synchronous client-device SAM
 * request function with return value.
 * @name:  Name of the generated function.
 * @rtype: Type of the request's return value.
 * @spec:  Specification (&struct ssam_request_spec_md) defining the request.
 *
 * Defines a function executing the synchronous SAM request specified by
 * @spec, with the request taking no argument but having a return value of
 * type @rtype. Device specifying parameters are not hard-coded, but instead
 * are provided via the client device, specifically its UID, supplied when
 * calling this function. The generated function takes care of setting up the
 * request struct, buffer allocation, as well as execution of the request
 * itself, returning once the request has been fully completed. The required
 * transport buffer will be allocated on the stack.
 *
 * The generated function is defined as ``static int name(struct ssam_device
 * *sdev, rtype *ret)``, returning the status of the request, which is zero on
 * success and negative on failure. The ``sdev`` parameter specifies both the
 * target device of the request and by association the controller via which
 * the request is sent. The request's return value is written to the memory
 * pointed to by the ``ret`` parameter.
 *
 * Refer to ssam_request_sync_onstack() for more details on the behavior of
 * the generated function.
 */
#define SSAM_DEFINE_SYNC_REQUEST_CL_R(name, rtype, spec...)		\
	SSAM_DEFINE_SYNC_REQUEST_MD_R(__raw_##name, rtype, spec)	\
	static int name(struct ssam_device *sdev, rtype *ret)		\
	{								\
		return __raw_##name(sdev->ctrl, sdev->uid.target,	\
				    sdev->uid.instance, ret);		\
	}

/**
 * SSAM_DEFINE_SYNC_REQUEST_CL_WR() - Define synchronous client-device SAM
 * request function with argument and return value.
 * @name:  Name of the generated function.
 * @atype: Type of the request's argument.
 * @rtype: Type of the request's return value.
 * @spec:  Specification (&struct ssam_request_spec_md) defining the request.
 *
 * Defines a function executing the synchronous SAM request specified by @spec,
 * with the request taking an argument of type @atype and having a return value
 * of type @rtype. Device specifying parameters are not hard-coded, but instead
 * are provided via the client device, specifically its UID, supplied when
 * calling this function. The generated function takes care of setting up the
 * request struct, buffer allocation, as well as execution of the request
 * itself, returning once the request has been fully completed. The required
 * transport buffer will be allocated on the stack.
 *
 * The generated function is defined as ``static int name(struct ssam_device
 * *sdev, const atype *arg, rtype *ret)``, returning the status of the request,
 * which is zero on success and negative on failure. The ``sdev`` parameter
 * specifies both the target device of the request and by association the
 * controller via which the request is sent. The request's argument is
 * specified via the ``arg`` pointer. The request's return value is written to
 * the memory pointed to by the ``ret`` parameter.
 *
 * Refer to ssam_request_sync_onstack() for more details on the behavior of
 * the generated function.
 */
#define SSAM_DEFINE_SYNC_REQUEST_CL_WR(name, atype, rtype, spec...)		\
	SSAM_DEFINE_SYNC_REQUEST_MD_WR(__raw_##name, atype, rtype, spec)	\
	static int name(struct ssam_device *sdev, const atype *arg, rtype *ret)	\
	{									\
		return __raw_##name(sdev->ctrl, sdev->uid.target,		\
				    sdev->uid.instance, arg, ret);		\
	}


/* -- Helpers for client-device notifiers. ---------------------------------- */

/**
 * ssam_device_notifier_register() - Register an event notifier for the
 * specified client device.
 * @sdev: The device the notifier should be registered on.
 * @n:    The event notifier to register.
 *
 * Register an event notifier. Increment the usage counter of the associated
 * SAM event if the notifier is not marked as an observer. If the event is not
 * marked as an observer and is currently not enabled, it will be enabled
 * during this call. If the notifier is marked as an observer, no attempt will
 * be made at enabling any event and no reference count will be modified.
 *
 * Notifiers marked as observers do not need to be associated with one specific
 * event, i.e. as long as no event matching is performed, only the event target
 * category needs to be set.
 *
 * Return: Returns zero on success, %-ENOSPC if there have already been
 * %INT_MAX notifiers for the event ID/type associated with the notifier block
 * registered, %-ENOMEM if the corresponding event entry could not be
 * allocated, %-ENODEV if the device is marked as hot-removed. If this is the
 * first time that a notifier block is registered for the specific associated
 * event, returns the status of the event-enable EC-command.
 */
static inline int ssam_device_notifier_register(struct ssam_device *sdev,
						struct ssam_event_notifier *n)
{
	/*
	 * Note that this check does not provide any guarantees whatsoever as
	 * hot-removal could happen at any point and we can't protect against
	 * it. Nevertheless, if we can detect hot-removal, bail early to avoid
	 * communication timeouts.
	 */
	if (ssam_device_is_hot_removed(sdev))
		return -ENODEV;

	return ssam_notifier_register(sdev->ctrl, n);
}

/**
 * ssam_device_notifier_unregister() - Unregister an event notifier for the
 * specified client device.
 * @sdev: The device the notifier has been registered on.
 * @n:    The event notifier to unregister.
 *
 * Unregister an event notifier. Decrement the usage counter of the associated
 * SAM event if the notifier is not marked as an observer. If the usage counter
 * reaches zero, the event will be disabled.
 *
 * In case the device has been marked as hot-removed, the event will not be
 * disabled on the EC, as in those cases any attempt at doing so may time out.
 *
 * Return: Returns zero on success, %-ENOENT if the given notifier block has
 * not been registered on the controller. If the given notifier block was the
 * last one associated with its specific event, returns the status of the
 * event-disable EC-command.
 */
static inline int ssam_device_notifier_unregister(struct ssam_device *sdev,
						  struct ssam_event_notifier *n)
{
	return __ssam_notifier_unregister(sdev->ctrl, n,
					  !ssam_device_is_hot_removed(sdev));
}

#endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */