Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
  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
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Surface System Aggregator Module (SSAM) controller interface.
 *
 * Main communication interface for the SSAM EC. Provides a controller
 * managing access and communication to and from the SSAM EC, as well as main
 * communication structures and definitions.
 *
 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
 */

#ifndef _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H
#define _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H

#include <linux/completion.h>
#include <linux/device.h>
#include <linux/types.h>

#include <linux/surface_aggregator/serial_hub.h>


/* -- Main data types and definitions --------------------------------------- */

/**
 * enum ssam_event_flags - Flags for enabling/disabling SAM events
 * @SSAM_EVENT_SEQUENCED: The event will be sent via a sequenced data frame.
 */
enum ssam_event_flags {
	SSAM_EVENT_SEQUENCED = BIT(0),
};

/**
 * struct ssam_event - SAM event sent from the EC to the host.
 * @target_category: Target category of the event source. See &enum ssam_ssh_tc.
 * @target_id:       Target ID of the event source.
 * @command_id:      Command ID of the event.
 * @instance_id:     Instance ID of the event source.
 * @length:          Length of the event payload in bytes.
 * @data:            Event payload data.
 */
struct ssam_event {
	u8 target_category;
	u8 target_id;
	u8 command_id;
	u8 instance_id;
	u16 length;
	u8 data[];
};

/**
 * enum ssam_request_flags - Flags for SAM requests.
 *
 * @SSAM_REQUEST_HAS_RESPONSE:
 *	Specifies that the request expects a response. If not set, the request
 *	will be directly completed after its underlying packet has been
 *	transmitted. If set, the request transport system waits for a response
 *	of the request.
 *
 * @SSAM_REQUEST_UNSEQUENCED:
 *	Specifies that the request should be transmitted via an unsequenced
 *	packet. If set, the request must not have a response, meaning that this
 *	flag and the %SSAM_REQUEST_HAS_RESPONSE flag are mutually exclusive.
 */
enum ssam_request_flags {
	SSAM_REQUEST_HAS_RESPONSE = BIT(0),
	SSAM_REQUEST_UNSEQUENCED  = BIT(1),
};

/**
 * struct ssam_request - SAM request description.
 * @target_category: Category of the request's target. See &enum ssam_ssh_tc.
 * @target_id:       ID of the request's target.
 * @command_id:      Command ID of the request.
 * @instance_id:     Instance ID of the request's target.
 * @flags:           Flags for the request. See &enum ssam_request_flags.
 * @length:          Length of the request payload in bytes.
 * @payload:         Request payload data.
 *
 * This struct fully describes a SAM request with payload. It is intended to
 * help set up the actual transport struct, e.g. &struct ssam_request_sync,
 * and specifically its raw message data via ssam_request_write_data().
 */
struct ssam_request {
	u8 target_category;
	u8 target_id;
	u8 command_id;
	u8 instance_id;
	u16 flags;
	u16 length;
	const u8 *payload;
};

/**
 * struct ssam_response - Response buffer for SAM request.
 * @capacity: Capacity of the buffer, in bytes.
 * @length:   Length of the actual data stored in the memory pointed to by
 *            @pointer, in bytes. Set by the transport system.
 * @pointer:  Pointer to the buffer's memory, storing the response payload data.
 */
struct ssam_response {
	size_t capacity;
	size_t length;
	u8 *pointer;
};

struct ssam_controller;

struct ssam_controller *ssam_get_controller(void);
struct ssam_controller *ssam_client_bind(struct device *client);
int ssam_client_link(struct ssam_controller *ctrl, struct device *client);

struct device *ssam_controller_device(struct ssam_controller *c);

struct ssam_controller *ssam_controller_get(struct ssam_controller *c);
void ssam_controller_put(struct ssam_controller *c);

void ssam_controller_statelock(struct ssam_controller *c);
void ssam_controller_stateunlock(struct ssam_controller *c);

ssize_t ssam_request_write_data(struct ssam_span *buf,
				struct ssam_controller *ctrl,
				const struct ssam_request *spec);


/* -- Synchronous request interface. ---------------------------------------- */

/**
 * struct ssam_request_sync - Synchronous SAM request struct.
 * @base:   Underlying SSH request.
 * @comp:   Completion used to signal full completion of the request. After the
 *          request has been submitted, this struct may only be modified or
 *          deallocated after the completion has been signaled.
 *          request has been submitted,
 * @resp:   Buffer to store the response.
 * @status: Status of the request, set after the base request has been
 *          completed or has failed.
 */
struct ssam_request_sync {
	struct ssh_request base;
	struct completion comp;
	struct ssam_response *resp;
	int status;
};

int ssam_request_sync_alloc(size_t payload_len, gfp_t flags,
			    struct ssam_request_sync **rqst,
			    struct ssam_span *buffer);

void ssam_request_sync_free(struct ssam_request_sync *rqst);

int ssam_request_sync_init(struct ssam_request_sync *rqst,
			   enum ssam_request_flags flags);

/**
 * ssam_request_sync_set_data - Set message data of a synchronous request.
 * @rqst: The request.
 * @ptr:  Pointer to the request message data.
 * @len:  Length of the request message data.
 *
 * Set the request message data of a synchronous request. The provided buffer
 * needs to live until the request has been completed.
 */
static inline void ssam_request_sync_set_data(struct ssam_request_sync *rqst,
					      u8 *ptr, size_t len)
{
	ssh_request_set_data(&rqst->base, ptr, len);
}

/**
 * ssam_request_sync_set_resp - Set response buffer of a synchronous request.
 * @rqst: The request.
 * @resp: The response buffer.
 *
 * Sets the response buffer of a synchronous request. This buffer will store
 * the response of the request after it has been completed. May be %NULL if no
 * response is expected.
 */
static inline void ssam_request_sync_set_resp(struct ssam_request_sync *rqst,
					      struct ssam_response *resp)
{
	rqst->resp = resp;
}

int ssam_request_sync_submit(struct ssam_controller *ctrl,
			     struct ssam_request_sync *rqst);

/**
 * ssam_request_sync_wait - Wait for completion of a synchronous request.
 * @rqst: The request to wait for.
 *
 * Wait for completion and release of a synchronous request. After this
 * function terminates, the request is guaranteed to have left the transport
 * system. After successful submission of a request, this function must be
 * called before accessing the response of the request, freeing the request,
 * or freeing any of the buffers associated with the request.
 *
 * This function must not be called if the request has not been submitted yet
 * and may lead to a deadlock/infinite wait if a subsequent request submission
 * fails in that case, due to the completion never triggering.
 *
 * Return: Returns the status of the given request, which is set on completion
 * of the packet. This value is zero on success and negative on failure.
 */
static inline int ssam_request_sync_wait(struct ssam_request_sync *rqst)
{
	wait_for_completion(&rqst->comp);
	return rqst->status;
}

int ssam_request_sync(struct ssam_controller *ctrl,
		      const struct ssam_request *spec,
		      struct ssam_response *rsp);

int ssam_request_sync_with_buffer(struct ssam_controller *ctrl,
				  const struct ssam_request *spec,
				  struct ssam_response *rsp,
				  struct ssam_span *buf);

/**
 * ssam_request_sync_onstack - Execute a synchronous request on the stack.
 * @ctrl: The controller via which the request is submitted.
 * @rqst: The request specification.
 * @rsp:  The response buffer.
 * @payload_len: The (maximum) request payload length.
 *
 * Allocates a synchronous request with specified payload length on the stack,
 * fully initializes it via the provided request specification, submits it,
 * and finally waits for its completion before returning its status. This
 * helper macro essentially allocates the request message buffer on the stack
 * and then calls ssam_request_sync_with_buffer().
 *
 * Note: The @payload_len parameter specifies the maximum payload length, used
 * for buffer allocation. The actual payload length may be smaller.
 *
 * Return: Returns the status of the request or any failure during setup, i.e.
 * zero on success and a negative value on failure.
 */
#define ssam_request_sync_onstack(ctrl, rqst, rsp, payload_len)			\
	({									\
		u8 __data[SSH_COMMAND_MESSAGE_LENGTH(payload_len)];		\
		struct ssam_span __buf = { &__data[0], ARRAY_SIZE(__data) };	\
										\
		ssam_request_sync_with_buffer(ctrl, rqst, rsp, &__buf);		\
	})

/**
 * __ssam_retry - Retry request in case of I/O errors or timeouts.
 * @request: The request function to execute. Must return an integer.
 * @n:       Number of tries.
 * @args:    Arguments for the request function.
 *
 * Executes the given request function, i.e. calls @request. In case the
 * request returns %-EREMOTEIO (indicates I/O error) or %-ETIMEDOUT (request
 * or underlying packet timed out), @request will be re-executed again, up to
 * @n times in total.
 *
 * Return: Returns the return value of the last execution of @request.
 */
#define __ssam_retry(request, n, args...)				\
	({								\
		int __i, __s = 0;					\
									\
		for (__i = (n); __i > 0; __i--) {			\
			__s = request(args);				\
			if (__s != -ETIMEDOUT && __s != -EREMOTEIO)	\
				break;					\
		}							\
		__s;							\
	})

/**
 * ssam_retry - Retry request in case of I/O errors or timeouts up to three
 * times in total.
 * @request: The request function to execute. Must return an integer.
 * @args:    Arguments for the request function.
 *
 * Executes the given request function, i.e. calls @request. In case the
 * request returns %-EREMOTEIO (indicates I/O error) or -%ETIMEDOUT (request
 * or underlying packet timed out), @request will be re-executed again, up to
 * three times in total.
 *
 * See __ssam_retry() for a more generic macro for this purpose.
 *
 * Return: Returns the return value of the last execution of @request.
 */
#define ssam_retry(request, args...) \
	__ssam_retry(request, 3, args)

/**
 * struct ssam_request_spec - Blue-print specification of SAM request.
 * @target_category: Category of the request's target. See &enum ssam_ssh_tc.
 * @target_id:       ID of the request's target.
 * @command_id:      Command ID of the request.
 * @instance_id:     Instance ID of the request's target.
 * @flags:           Flags for the request. See &enum ssam_request_flags.
 *
 * Blue-print specification for a SAM request. This struct describes the
 * unique static parameters of a request (i.e. type) without specifying any of
 * its instance-specific data (e.g. payload). It is intended to be used as base
 * for defining simple request functions via the
 * ``SSAM_DEFINE_SYNC_REQUEST_x()`` family of macros.
 */
struct ssam_request_spec {
	u8 target_category;
	u8 target_id;
	u8 command_id;
	u8 instance_id;
	u8 flags;
};

/**
 * struct ssam_request_spec_md - Blue-print specification for multi-device SAM
 * request.
 * @target_category: Category of the request's target. See &enum ssam_ssh_tc.
 * @command_id:      Command ID of the request.
 * @flags:           Flags for the request. See &enum ssam_request_flags.
 *
 * Blue-print specification for a multi-device SAM request, i.e. a request
 * that is applicable to multiple device instances, described by their
 * individual target and instance IDs. This struct describes the unique static
 * parameters of a request (i.e. type) without specifying any of its
 * instance-specific data (e.g. payload) and without specifying any of its
 * device specific IDs (i.e. target and instance ID). It is intended to be
 * used as base for defining simple multi-device request functions via the
 * ``SSAM_DEFINE_SYNC_REQUEST_MD_x()`` and ``SSAM_DEFINE_SYNC_REQUEST_CL_x()``
 * families of macros.
 */
struct ssam_request_spec_md {
	u8 target_category;
	u8 command_id;
	u8 flags;
};

/**
 * SSAM_DEFINE_SYNC_REQUEST_N() - Define synchronous SAM request function
 * with neither argument nor return value.
 * @name: Name of the generated function.
 * @spec: Specification (&struct ssam_request_spec) defining the request.
 *
 * Defines a function executing the synchronous SAM request specified by
 * @spec, with the request having neither argument nor return value. The
 * generated function takes care of setting up the request struct and 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_controller *ctrl)``, returning the status of the request, which is
 * zero on success and negative on failure. The ``ctrl`` parameter is the
 * controller via which the request is being sent.
 *
 * Refer to ssam_request_sync_onstack() for more details on the behavior of
 * the generated function.
 */
#define SSAM_DEFINE_SYNC_REQUEST_N(name, spec...)				\
	static int name(struct ssam_controller *ctrl)				\
	{									\
		struct ssam_request_spec s = (struct ssam_request_spec)spec;	\
		struct ssam_request rqst;					\
										\
		rqst.target_category = s.target_category;			\
		rqst.target_id = s.target_id;					\
		rqst.command_id = s.command_id;					\
		rqst.instance_id = s.instance_id;				\
		rqst.flags = s.flags;						\
		rqst.length = 0;						\
		rqst.payload = NULL;						\
										\
		return ssam_request_sync_onstack(ctrl, &rqst, NULL, 0);		\
	}

/**
 * SSAM_DEFINE_SYNC_REQUEST_W() - Define synchronous SAM request function with
 * argument.
 * @name:  Name of the generated function.
 * @atype: Type of the request's argument.
 * @spec:  Specification (&struct ssam_request_spec) 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. 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_controller *ctrl, const atype *arg)``, returning the status of the
 * request, which is zero on success and negative on failure. The ``ctrl``
 * parameter is the controller via which the request is sent. The request
 * 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_W(name, atype, spec...)			\
	static int name(struct ssam_controller *ctrl, const atype *arg)		\
	{									\
		struct ssam_request_spec s = (struct ssam_request_spec)spec;	\
		struct ssam_request rqst;					\
										\
		rqst.target_category = s.target_category;			\
		rqst.target_id = s.target_id;					\
		rqst.command_id = s.command_id;					\
		rqst.instance_id = s.instance_id;				\
		rqst.flags = s.flags;						\
		rqst.length = sizeof(atype);					\
		rqst.payload = (u8 *)arg;					\
										\
		return ssam_request_sync_onstack(ctrl, &rqst, NULL,		\
						 sizeof(atype));		\
	}

/**
 * SSAM_DEFINE_SYNC_REQUEST_R() - Define synchronous 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) 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. The generated function takes care of setting up the request
 * and response structs, 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_controller *ctrl, rtype *ret)``, returning the status of the request,
 * which is zero on success and negative on failure. The ``ctrl`` parameter is
 * 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_R(name, rtype, spec...)			\
	static int name(struct ssam_controller *ctrl, rtype *ret)		\
	{									\
		struct ssam_request_spec s = (struct ssam_request_spec)spec;	\
		struct ssam_request rqst;					\
		struct ssam_response rsp;					\
		int status;							\
										\
		rqst.target_category = s.target_category;			\
		rqst.target_id = s.target_id;					\
		rqst.command_id = s.command_id;					\
		rqst.instance_id = s.instance_id;				\
		rqst.flags = s.flags | SSAM_REQUEST_HAS_RESPONSE;		\
		rqst.length = 0;						\
		rqst.payload = NULL;						\
										\
		rsp.capacity = sizeof(rtype);					\
		rsp.length = 0;							\
		rsp.pointer = (u8 *)ret;					\
										\
		status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, 0);	\
		if (status)							\
			return status;						\
										\
		if (rsp.length != sizeof(rtype)) {				\
			struct device *dev = ssam_controller_device(ctrl);	\
			dev_err(dev,						\
				"rqst: invalid response length, expected %zu, got %zu (tc: %#04x, cid: %#04x)", \
				sizeof(rtype), rsp.length, rqst.target_category,\
				rqst.command_id);				\
			return -EIO;						\
		}								\
										\
		return 0;							\
	}

/**
 * SSAM_DEFINE_SYNC_REQUEST_MD_N() - Define synchronous multi-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 must be provided to
 * the 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_controller *ctrl, u8 tid, u8 iid)``, returning the status of the
 * request, which is zero on success and negative on failure. The ``ctrl``
 * parameter is the controller via which the request is sent, ``tid`` the
 * target ID for the request, and ``iid`` the instance ID.
 *
 * Refer to ssam_request_sync_onstack() for more details on the behavior of
 * the generated function.
 */
#define SSAM_DEFINE_SYNC_REQUEST_MD_N(name, spec...)				\
	static int name(struct ssam_controller *ctrl, u8 tid, u8 iid)		\
	{									\
		struct ssam_request_spec_md s = (struct ssam_request_spec_md)spec; \
		struct ssam_request rqst;					\
										\
		rqst.target_category = s.target_category;			\
		rqst.target_id = tid;						\
		rqst.command_id = s.command_id;					\
		rqst.instance_id = iid;						\
		rqst.flags = s.flags;						\
		rqst.length = 0;						\
		rqst.payload = NULL;						\
										\
		return ssam_request_sync_onstack(ctrl, &rqst, NULL, 0);		\
	}

/**
 * SSAM_DEFINE_SYNC_REQUEST_MD_W() - Define synchronous multi-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
 * must be provided to the 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_controller *ctrl, u8 tid, u8 iid, const atype *arg)``, returning the
 * status of the request, which is zero on success and negative on failure.
 * The ``ctrl`` parameter is the controller via which the request is sent,
 * ``tid`` the target ID for the request, and ``iid`` the instance ID. The
 * request 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_MD_W(name, atype, spec...)			\
	static int name(struct ssam_controller *ctrl, u8 tid, u8 iid, const atype *arg) \
	{									\
		struct ssam_request_spec_md s = (struct ssam_request_spec_md)spec; \
		struct ssam_request rqst;					\
										\
		rqst.target_category = s.target_category;			\
		rqst.target_id = tid;						\
		rqst.command_id = s.command_id;					\
		rqst.instance_id = iid;						\
		rqst.flags = s.flags;						\
		rqst.length = sizeof(atype);					\
		rqst.payload = (u8 *)arg;					\
										\
		return ssam_request_sync_onstack(ctrl, &rqst, NULL,		\
						 sizeof(atype));		\
	}

/**
 * SSAM_DEFINE_SYNC_REQUEST_MD_R() - Define synchronous multi-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
 * must be provided to the function. The generated function takes care of
 * setting up the request and response structs, 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_controller *ctrl, u8 tid, u8 iid, rtype *ret)``, returning the status
 * of the request, which is zero on success and negative on failure. The
 * ``ctrl`` parameter is the controller via which the request is sent, ``tid``
 * the target ID for the request, and ``iid`` the instance ID. 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_MD_R(name, rtype, spec...)			\
	static int name(struct ssam_controller *ctrl, u8 tid, u8 iid, rtype *ret) \
	{									\
		struct ssam_request_spec_md s = (struct ssam_request_spec_md)spec; \
		struct ssam_request rqst;					\
		struct ssam_response rsp;					\
		int status;							\
										\
		rqst.target_category = s.target_category;			\
		rqst.target_id = tid;						\
		rqst.command_id = s.command_id;					\
		rqst.instance_id = iid;						\
		rqst.flags = s.flags | SSAM_REQUEST_HAS_RESPONSE;		\
		rqst.length = 0;						\
		rqst.payload = NULL;						\
										\
		rsp.capacity = sizeof(rtype);					\
		rsp.length = 0;							\
		rsp.pointer = (u8 *)ret;					\
										\
		status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, 0);	\
		if (status)							\
			return status;						\
										\
		if (rsp.length != sizeof(rtype)) {				\
			struct device *dev = ssam_controller_device(ctrl);	\
			dev_err(dev,						\
				"rqst: invalid response length, expected %zu, got %zu (tc: %#04x, cid: %#04x)", \
				sizeof(rtype), rsp.length, rqst.target_category,\
				rqst.command_id);				\
			return -EIO;						\
		}								\
										\
		return 0;							\
	}


/* -- Event notifier/callbacks. --------------------------------------------- */

#define SSAM_NOTIF_STATE_SHIFT		2
#define SSAM_NOTIF_STATE_MASK		((1 << SSAM_NOTIF_STATE_SHIFT) - 1)

/**
 * enum ssam_notif_flags - Flags used in return values from SSAM notifier
 * callback functions.
 *
 * @SSAM_NOTIF_HANDLED:
 *	Indicates that the notification has been handled. This flag should be
 *	set by the handler if the handler can act/has acted upon the event
 *	provided to it. This flag should not be set if the handler is not a
 *	primary handler intended for the provided event.
 *
 *	If this flag has not been set by any handler after the notifier chain
 *	has been traversed, a warning will be emitted, stating that the event
 *	has not been handled.
 *
 * @SSAM_NOTIF_STOP:
 *	Indicates that the notifier traversal should stop. If this flag is
 *	returned from a notifier callback, notifier chain traversal will
 *	immediately stop and any remaining notifiers will not be called. This
 *	flag is automatically set when ssam_notifier_from_errno() is called
 *	with a negative error value.
 */
enum ssam_notif_flags {
	SSAM_NOTIF_HANDLED = BIT(0),
	SSAM_NOTIF_STOP    = BIT(1),
};

struct ssam_event_notifier;

typedef u32 (*ssam_notifier_fn_t)(struct ssam_event_notifier *nf,
				  const struct ssam_event *event);

/**
 * struct ssam_notifier_block - Base notifier block for SSAM event
 * notifications.
 * @node:     The node for the list of notifiers.
 * @fn:       The callback function of this notifier. This function takes the
 *            respective notifier block and event as input and should return
 *            a notifier value, which can either be obtained from the flags
 *            provided in &enum ssam_notif_flags, converted from a standard
 *            error value via ssam_notifier_from_errno(), or a combination of
 *            both (e.g. ``ssam_notifier_from_errno(e) | SSAM_NOTIF_HANDLED``).
 * @priority: Priority value determining the order in which notifier callbacks
 *            will be called. A higher value means higher priority, i.e. the
 *            associated callback will be executed earlier than other (lower
 *            priority) callbacks.
 */
struct ssam_notifier_block {
	struct list_head node;
	ssam_notifier_fn_t fn;
	int priority;
};

/**
 * ssam_notifier_from_errno() - Convert standard error value to notifier
 * return code.
 * @err: The error code to convert, must be negative (in case of failure) or
 *       zero (in case of success).
 *
 * Return: Returns the notifier return value obtained by converting the
 * specified @err value. In case @err is negative, the %SSAM_NOTIF_STOP flag
 * will be set, causing notifier call chain traversal to abort.
 */
static inline u32 ssam_notifier_from_errno(int err)
{
	if (WARN_ON(err > 0) || err == 0)
		return 0;
	else
		return ((-err) << SSAM_NOTIF_STATE_SHIFT) | SSAM_NOTIF_STOP;
}

/**
 * ssam_notifier_to_errno() - Convert notifier return code to standard error
 * value.
 * @ret: The notifier return value to convert.
 *
 * Return: Returns the negative error value encoded in @ret or zero if @ret
 * indicates success.
 */
static inline int ssam_notifier_to_errno(u32 ret)
{
	return -(ret >> SSAM_NOTIF_STATE_SHIFT);
}


/* -- Event/notification registry. ------------------------------------------ */

/**
 * struct ssam_event_registry - Registry specification used for enabling events.
 * @target_category: Target category for the event registry requests.
 * @target_id:       Target ID for the event registry requests.
 * @cid_enable:      Command ID for the event-enable request.
 * @cid_disable:     Command ID for the event-disable request.
 *
 * This struct describes a SAM event registry via the minimal collection of
 * SAM IDs specifying the requests to use for enabling and disabling an event.
 * The individual event to be enabled/disabled itself is specified via &struct
 * ssam_event_id.
 */
struct ssam_event_registry {
	u8 target_category;
	u8 target_id;
	u8 cid_enable;
	u8 cid_disable;
};

/**
 * struct ssam_event_id - Unique event ID used for enabling events.
 * @target_category: Target category of the event source.
 * @instance:        Instance ID of the event source.
 *
 * This struct specifies the event to be enabled/disabled via an externally
 * provided registry. It does not specify the registry to be used itself, this
 * is done via &struct ssam_event_registry.
 */
struct ssam_event_id {
	u8 target_category;
	u8 instance;
};

/**
 * enum ssam_event_mask - Flags specifying how events are matched to notifiers.
 *
 * @SSAM_EVENT_MASK_NONE:
 *	Run the callback for any event with matching target category. Do not
 *	do any additional filtering.
 *
 * @SSAM_EVENT_MASK_TARGET:
 *	In addition to filtering by target category, only execute the notifier
 *	callback for events with a target ID matching to the one of the
 *	registry used for enabling/disabling the event.
 *
 * @SSAM_EVENT_MASK_INSTANCE:
 *	In addition to filtering by target category, only execute the notifier
 *	callback for events with an instance ID matching to the instance ID
 *	used when enabling the event.
 *
 * @SSAM_EVENT_MASK_STRICT:
 *	Do all the filtering above.
 */
enum ssam_event_mask {
	SSAM_EVENT_MASK_TARGET   = BIT(0),
	SSAM_EVENT_MASK_INSTANCE = BIT(1),

	SSAM_EVENT_MASK_NONE = 0,
	SSAM_EVENT_MASK_STRICT =
		  SSAM_EVENT_MASK_TARGET
		| SSAM_EVENT_MASK_INSTANCE,
};

/**
 * SSAM_EVENT_REGISTRY() - Define a new event registry.
 * @tc:      Target category for the event registry requests.
 * @tid:     Target ID for the event registry requests.
 * @cid_en:  Command ID for the event-enable request.
 * @cid_dis: Command ID for the event-disable request.
 *
 * Return: Returns the &struct ssam_event_registry specified by the given
 * parameters.
 */
#define SSAM_EVENT_REGISTRY(tc, tid, cid_en, cid_dis)	\
	((struct ssam_event_registry) {			\
		.target_category = (tc),		\
		.target_id = (tid),			\
		.cid_enable = (cid_en),			\
		.cid_disable = (cid_dis),		\
	})

#define SSAM_EVENT_REGISTRY_SAM	\
	SSAM_EVENT_REGISTRY(SSAM_SSH_TC_SAM, 0x01, 0x0b, 0x0c)

#define SSAM_EVENT_REGISTRY_KIP	\
	SSAM_EVENT_REGISTRY(SSAM_SSH_TC_KIP, 0x02, 0x27, 0x28)

#define SSAM_EVENT_REGISTRY_REG \
	SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, 0x02, 0x01, 0x02)

/**
 * enum ssam_event_notifier_flags - Flags for event notifiers.
 * @SSAM_EVENT_NOTIFIER_OBSERVER:
 *	The corresponding notifier acts as observer. Registering a notifier
 *	with this flag set will not attempt to enable any event. Equally,
 *	unregistering will not attempt to disable any event. Note that a
 *	notifier with this flag may not even correspond to a certain event at
 *	all, only to a specific event target category. Event matching will not
 *	be influenced by this flag.
 */
enum ssam_event_notifier_flags {
	SSAM_EVENT_NOTIFIER_OBSERVER = BIT(0),
};

/**
 * struct ssam_event_notifier - Notifier block for SSAM events.
 * @base:        The base notifier block with callback function and priority.
 * @event:       The event for which this block will receive notifications.
 * @event.reg:   Registry via which the event will be enabled/disabled.
 * @event.id:    ID specifying the event.
 * @event.mask:  Flags determining how events are matched to the notifier.
 * @event.flags: Flags used for enabling the event.
 * @flags:       Notifier flags (see &enum ssam_event_notifier_flags).
 */
struct ssam_event_notifier {
	struct ssam_notifier_block base;

	struct {
		struct ssam_event_registry reg;
		struct ssam_event_id id;
		enum ssam_event_mask mask;
		u8 flags;
	} event;

	unsigned long flags;
};

int ssam_notifier_register(struct ssam_controller *ctrl,
			   struct ssam_event_notifier *n);

int ssam_notifier_unregister(struct ssam_controller *ctrl,
			     struct ssam_event_notifier *n);

int ssam_controller_event_enable(struct ssam_controller *ctrl,
				 struct ssam_event_registry reg,
				 struct ssam_event_id id, u8 flags);

int ssam_controller_event_disable(struct ssam_controller *ctrl,
				  struct ssam_event_registry reg,
				  struct ssam_event_id id, u8 flags);

#endif /* _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H */