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
|
/* 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/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)
/**
* struct ssam_device - SSAM client device.
* @dev: Driver model representation of the device.
* @ctrl: SSAM controller managing this device.
* @uid: UID identifying the device.
*/
struct ssam_device {
struct device dev;
struct ssam_controller *ctrl;
struct ssam_device_uid uid;
};
/**
* 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);
};
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;
}
/**
* 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_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
void ssam_remove_clients(struct device *dev);
#else /* CONFIG_SURFACE_AGGREGATOR_BUS */
static inline void ssam_remove_clients(struct device *dev) {}
#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */
/* -- 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); \
}
#endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */
|