cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

surface_aggregator_cdev.c (20950B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Provides user-space access to the SSAM EC via the /dev/surface/aggregator
      4 * misc device. Intended for debugging and development.
      5 *
      6 * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
      7 */
      8
      9#include <linux/fs.h>
     10#include <linux/ioctl.h>
     11#include <linux/kernel.h>
     12#include <linux/kfifo.h>
     13#include <linux/kref.h>
     14#include <linux/miscdevice.h>
     15#include <linux/module.h>
     16#include <linux/platform_device.h>
     17#include <linux/poll.h>
     18#include <linux/rwsem.h>
     19#include <linux/slab.h>
     20#include <linux/uaccess.h>
     21#include <linux/vmalloc.h>
     22
     23#include <linux/surface_aggregator/cdev.h>
     24#include <linux/surface_aggregator/controller.h>
     25#include <linux/surface_aggregator/serial_hub.h>
     26
     27#define SSAM_CDEV_DEVICE_NAME	"surface_aggregator_cdev"
     28
     29
     30/* -- Main structures. ------------------------------------------------------ */
     31
     32enum ssam_cdev_device_state {
     33	SSAM_CDEV_DEVICE_SHUTDOWN_BIT = BIT(0),
     34};
     35
     36struct ssam_cdev {
     37	struct kref kref;
     38	struct rw_semaphore lock;
     39
     40	struct device *dev;
     41	struct ssam_controller *ctrl;
     42	struct miscdevice mdev;
     43	unsigned long flags;
     44
     45	struct rw_semaphore client_lock;  /* Guards client list. */
     46	struct list_head client_list;
     47};
     48
     49struct ssam_cdev_client;
     50
     51struct ssam_cdev_notifier {
     52	struct ssam_cdev_client *client;
     53	struct ssam_event_notifier nf;
     54};
     55
     56struct ssam_cdev_client {
     57	struct ssam_cdev *cdev;
     58	struct list_head node;
     59
     60	struct mutex notifier_lock;	/* Guards notifier access for registration */
     61	struct ssam_cdev_notifier *notifier[SSH_NUM_EVENTS];
     62
     63	struct mutex read_lock;		/* Guards FIFO buffer read access */
     64	struct mutex write_lock;	/* Guards FIFO buffer write access */
     65	DECLARE_KFIFO(buffer, u8, 4096);
     66
     67	wait_queue_head_t waitq;
     68	struct fasync_struct *fasync;
     69};
     70
     71static void __ssam_cdev_release(struct kref *kref)
     72{
     73	kfree(container_of(kref, struct ssam_cdev, kref));
     74}
     75
     76static struct ssam_cdev *ssam_cdev_get(struct ssam_cdev *cdev)
     77{
     78	if (cdev)
     79		kref_get(&cdev->kref);
     80
     81	return cdev;
     82}
     83
     84static void ssam_cdev_put(struct ssam_cdev *cdev)
     85{
     86	if (cdev)
     87		kref_put(&cdev->kref, __ssam_cdev_release);
     88}
     89
     90
     91/* -- Notifier handling. ---------------------------------------------------- */
     92
     93static u32 ssam_cdev_notifier(struct ssam_event_notifier *nf, const struct ssam_event *in)
     94{
     95	struct ssam_cdev_notifier *cdev_nf = container_of(nf, struct ssam_cdev_notifier, nf);
     96	struct ssam_cdev_client *client = cdev_nf->client;
     97	struct ssam_cdev_event event;
     98	size_t n = struct_size(&event, data, in->length);
     99
    100	/* Translate event. */
    101	event.target_category = in->target_category;
    102	event.target_id = in->target_id;
    103	event.command_id = in->command_id;
    104	event.instance_id = in->instance_id;
    105	event.length = in->length;
    106
    107	mutex_lock(&client->write_lock);
    108
    109	/* Make sure we have enough space. */
    110	if (kfifo_avail(&client->buffer) < n) {
    111		dev_warn(client->cdev->dev,
    112			 "buffer full, dropping event (tc: %#04x, tid: %#04x, cid: %#04x, iid: %#04x)\n",
    113			 in->target_category, in->target_id, in->command_id, in->instance_id);
    114		mutex_unlock(&client->write_lock);
    115		return 0;
    116	}
    117
    118	/* Copy event header and payload. */
    119	kfifo_in(&client->buffer, (const u8 *)&event, struct_size(&event, data, 0));
    120	kfifo_in(&client->buffer, &in->data[0], in->length);
    121
    122	mutex_unlock(&client->write_lock);
    123
    124	/* Notify waiting readers. */
    125	kill_fasync(&client->fasync, SIGIO, POLL_IN);
    126	wake_up_interruptible(&client->waitq);
    127
    128	/*
    129	 * Don't mark events as handled, this is the job of a proper driver and
    130	 * not the debugging interface.
    131	 */
    132	return 0;
    133}
    134
    135static int ssam_cdev_notifier_register(struct ssam_cdev_client *client, u8 tc, int priority)
    136{
    137	const u16 rqid = ssh_tc_to_rqid(tc);
    138	const u16 event = ssh_rqid_to_event(rqid);
    139	struct ssam_cdev_notifier *nf;
    140	int status;
    141
    142	lockdep_assert_held_read(&client->cdev->lock);
    143
    144	/* Validate notifier target category. */
    145	if (!ssh_rqid_is_event(rqid))
    146		return -EINVAL;
    147
    148	mutex_lock(&client->notifier_lock);
    149
    150	/* Check if the notifier has already been registered. */
    151	if (client->notifier[event]) {
    152		mutex_unlock(&client->notifier_lock);
    153		return -EEXIST;
    154	}
    155
    156	/* Allocate new notifier. */
    157	nf = kzalloc(sizeof(*nf), GFP_KERNEL);
    158	if (!nf) {
    159		mutex_unlock(&client->notifier_lock);
    160		return -ENOMEM;
    161	}
    162
    163	/*
    164	 * Create a dummy notifier with the minimal required fields for
    165	 * observer registration. Note that we can skip fully specifying event
    166	 * and registry here as we do not need any matching and use silent
    167	 * registration, which does not enable the corresponding event.
    168	 */
    169	nf->client = client;
    170	nf->nf.base.fn = ssam_cdev_notifier;
    171	nf->nf.base.priority = priority;
    172	nf->nf.event.id.target_category = tc;
    173	nf->nf.event.mask = 0;	/* Do not do any matching. */
    174	nf->nf.flags = SSAM_EVENT_NOTIFIER_OBSERVER;
    175
    176	/* Register notifier. */
    177	status = ssam_notifier_register(client->cdev->ctrl, &nf->nf);
    178	if (status)
    179		kfree(nf);
    180	else
    181		client->notifier[event] = nf;
    182
    183	mutex_unlock(&client->notifier_lock);
    184	return status;
    185}
    186
    187static int ssam_cdev_notifier_unregister(struct ssam_cdev_client *client, u8 tc)
    188{
    189	const u16 rqid = ssh_tc_to_rqid(tc);
    190	const u16 event = ssh_rqid_to_event(rqid);
    191	int status;
    192
    193	lockdep_assert_held_read(&client->cdev->lock);
    194
    195	/* Validate notifier target category. */
    196	if (!ssh_rqid_is_event(rqid))
    197		return -EINVAL;
    198
    199	mutex_lock(&client->notifier_lock);
    200
    201	/* Check if the notifier is currently registered. */
    202	if (!client->notifier[event]) {
    203		mutex_unlock(&client->notifier_lock);
    204		return -ENOENT;
    205	}
    206
    207	/* Unregister and free notifier. */
    208	status = ssam_notifier_unregister(client->cdev->ctrl, &client->notifier[event]->nf);
    209	kfree(client->notifier[event]);
    210	client->notifier[event] = NULL;
    211
    212	mutex_unlock(&client->notifier_lock);
    213	return status;
    214}
    215
    216static void ssam_cdev_notifier_unregister_all(struct ssam_cdev_client *client)
    217{
    218	int i;
    219
    220	down_read(&client->cdev->lock);
    221
    222	/*
    223	 * This function may be used during shutdown, thus we need to test for
    224	 * cdev->ctrl instead of the SSAM_CDEV_DEVICE_SHUTDOWN_BIT bit.
    225	 */
    226	if (client->cdev->ctrl) {
    227		for (i = 0; i < SSH_NUM_EVENTS; i++)
    228			ssam_cdev_notifier_unregister(client, i + 1);
    229
    230	} else {
    231		int count = 0;
    232
    233		/*
    234		 * Device has been shut down. Any notifier remaining is a bug,
    235		 * so warn about that as this would otherwise hardly be
    236		 * noticeable. Nevertheless, free them as well.
    237		 */
    238		mutex_lock(&client->notifier_lock);
    239		for (i = 0; i < SSH_NUM_EVENTS; i++) {
    240			count += !!(client->notifier[i]);
    241			kfree(client->notifier[i]);
    242			client->notifier[i] = NULL;
    243		}
    244		mutex_unlock(&client->notifier_lock);
    245
    246		WARN_ON(count > 0);
    247	}
    248
    249	up_read(&client->cdev->lock);
    250}
    251
    252
    253/* -- IOCTL functions. ------------------------------------------------------ */
    254
    255static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_request __user *r)
    256{
    257	struct ssam_cdev_request rqst;
    258	struct ssam_request spec = {};
    259	struct ssam_response rsp = {};
    260	const void __user *plddata;
    261	void __user *rspdata;
    262	int status = 0, ret = 0, tmp;
    263
    264	lockdep_assert_held_read(&client->cdev->lock);
    265
    266	ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r));
    267	if (ret)
    268		goto out;
    269
    270	plddata = u64_to_user_ptr(rqst.payload.data);
    271	rspdata = u64_to_user_ptr(rqst.response.data);
    272
    273	/* Setup basic request fields. */
    274	spec.target_category = rqst.target_category;
    275	spec.target_id = rqst.target_id;
    276	spec.command_id = rqst.command_id;
    277	spec.instance_id = rqst.instance_id;
    278	spec.flags = 0;
    279	spec.length = rqst.payload.length;
    280	spec.payload = NULL;
    281
    282	if (rqst.flags & SSAM_CDEV_REQUEST_HAS_RESPONSE)
    283		spec.flags |= SSAM_REQUEST_HAS_RESPONSE;
    284
    285	if (rqst.flags & SSAM_CDEV_REQUEST_UNSEQUENCED)
    286		spec.flags |= SSAM_REQUEST_UNSEQUENCED;
    287
    288	rsp.capacity = rqst.response.length;
    289	rsp.length = 0;
    290	rsp.pointer = NULL;
    291
    292	/* Get request payload from user-space. */
    293	if (spec.length) {
    294		if (!plddata) {
    295			ret = -EINVAL;
    296			goto out;
    297		}
    298
    299		/*
    300		 * Note: spec.length is limited to U16_MAX bytes via struct
    301		 * ssam_cdev_request. This is slightly larger than the
    302		 * theoretical maximum (SSH_COMMAND_MAX_PAYLOAD_SIZE) of the
    303		 * underlying protocol (note that nothing remotely this size
    304		 * should ever be allocated in any normal case). This size is
    305		 * validated later in ssam_request_sync(), for allocation the
    306		 * bound imposed by u16 should be enough.
    307		 */
    308		spec.payload = kzalloc(spec.length, GFP_KERNEL);
    309		if (!spec.payload) {
    310			ret = -ENOMEM;
    311			goto out;
    312		}
    313
    314		if (copy_from_user((void *)spec.payload, plddata, spec.length)) {
    315			ret = -EFAULT;
    316			goto out;
    317		}
    318	}
    319
    320	/* Allocate response buffer. */
    321	if (rsp.capacity) {
    322		if (!rspdata) {
    323			ret = -EINVAL;
    324			goto out;
    325		}
    326
    327		/*
    328		 * Note: rsp.capacity is limited to U16_MAX bytes via struct
    329		 * ssam_cdev_request. This is slightly larger than the
    330		 * theoretical maximum (SSH_COMMAND_MAX_PAYLOAD_SIZE) of the
    331		 * underlying protocol (note that nothing remotely this size
    332		 * should ever be allocated in any normal case). In later use,
    333		 * this capacity does not have to be strictly bounded, as it
    334		 * is only used as an output buffer to be written to. For
    335		 * allocation the bound imposed by u16 should be enough.
    336		 */
    337		rsp.pointer = kzalloc(rsp.capacity, GFP_KERNEL);
    338		if (!rsp.pointer) {
    339			ret = -ENOMEM;
    340			goto out;
    341		}
    342	}
    343
    344	/* Perform request. */
    345	status = ssam_request_sync(client->cdev->ctrl, &spec, &rsp);
    346	if (status)
    347		goto out;
    348
    349	/* Copy response to user-space. */
    350	if (rsp.length && copy_to_user(rspdata, rsp.pointer, rsp.length))
    351		ret = -EFAULT;
    352
    353out:
    354	/* Always try to set response-length and status. */
    355	tmp = put_user(rsp.length, &r->response.length);
    356	if (tmp)
    357		ret = tmp;
    358
    359	tmp = put_user(status, &r->status);
    360	if (tmp)
    361		ret = tmp;
    362
    363	/* Cleanup. */
    364	kfree(spec.payload);
    365	kfree(rsp.pointer);
    366
    367	return ret;
    368}
    369
    370static long ssam_cdev_notif_register(struct ssam_cdev_client *client,
    371				     const struct ssam_cdev_notifier_desc __user *d)
    372{
    373	struct ssam_cdev_notifier_desc desc;
    374	long ret;
    375
    376	lockdep_assert_held_read(&client->cdev->lock);
    377
    378	ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
    379	if (ret)
    380		return ret;
    381
    382	return ssam_cdev_notifier_register(client, desc.target_category, desc.priority);
    383}
    384
    385static long ssam_cdev_notif_unregister(struct ssam_cdev_client *client,
    386				       const struct ssam_cdev_notifier_desc __user *d)
    387{
    388	struct ssam_cdev_notifier_desc desc;
    389	long ret;
    390
    391	lockdep_assert_held_read(&client->cdev->lock);
    392
    393	ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
    394	if (ret)
    395		return ret;
    396
    397	return ssam_cdev_notifier_unregister(client, desc.target_category);
    398}
    399
    400static long ssam_cdev_event_enable(struct ssam_cdev_client *client,
    401				   const struct ssam_cdev_event_desc __user *d)
    402{
    403	struct ssam_cdev_event_desc desc;
    404	struct ssam_event_registry reg;
    405	struct ssam_event_id id;
    406	long ret;
    407
    408	lockdep_assert_held_read(&client->cdev->lock);
    409
    410	/* Read descriptor from user-space. */
    411	ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
    412	if (ret)
    413		return ret;
    414
    415	/* Translate descriptor. */
    416	reg.target_category = desc.reg.target_category;
    417	reg.target_id = desc.reg.target_id;
    418	reg.cid_enable = desc.reg.cid_enable;
    419	reg.cid_disable = desc.reg.cid_disable;
    420
    421	id.target_category = desc.id.target_category;
    422	id.instance = desc.id.instance;
    423
    424	/* Disable event. */
    425	return ssam_controller_event_enable(client->cdev->ctrl, reg, id, desc.flags);
    426}
    427
    428static long ssam_cdev_event_disable(struct ssam_cdev_client *client,
    429				    const struct ssam_cdev_event_desc __user *d)
    430{
    431	struct ssam_cdev_event_desc desc;
    432	struct ssam_event_registry reg;
    433	struct ssam_event_id id;
    434	long ret;
    435
    436	lockdep_assert_held_read(&client->cdev->lock);
    437
    438	/* Read descriptor from user-space. */
    439	ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
    440	if (ret)
    441		return ret;
    442
    443	/* Translate descriptor. */
    444	reg.target_category = desc.reg.target_category;
    445	reg.target_id = desc.reg.target_id;
    446	reg.cid_enable = desc.reg.cid_enable;
    447	reg.cid_disable = desc.reg.cid_disable;
    448
    449	id.target_category = desc.id.target_category;
    450	id.instance = desc.id.instance;
    451
    452	/* Disable event. */
    453	return ssam_controller_event_disable(client->cdev->ctrl, reg, id, desc.flags);
    454}
    455
    456
    457/* -- File operations. ------------------------------------------------------ */
    458
    459static int ssam_cdev_device_open(struct inode *inode, struct file *filp)
    460{
    461	struct miscdevice *mdev = filp->private_data;
    462	struct ssam_cdev_client *client;
    463	struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev);
    464
    465	/* Initialize client */
    466	client = vzalloc(sizeof(*client));
    467	if (!client)
    468		return -ENOMEM;
    469
    470	client->cdev = ssam_cdev_get(cdev);
    471
    472	INIT_LIST_HEAD(&client->node);
    473
    474	mutex_init(&client->notifier_lock);
    475
    476	mutex_init(&client->read_lock);
    477	mutex_init(&client->write_lock);
    478	INIT_KFIFO(client->buffer);
    479	init_waitqueue_head(&client->waitq);
    480
    481	filp->private_data = client;
    482
    483	/* Attach client. */
    484	down_write(&cdev->client_lock);
    485
    486	if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
    487		up_write(&cdev->client_lock);
    488		mutex_destroy(&client->write_lock);
    489		mutex_destroy(&client->read_lock);
    490		mutex_destroy(&client->notifier_lock);
    491		ssam_cdev_put(client->cdev);
    492		vfree(client);
    493		return -ENODEV;
    494	}
    495	list_add_tail(&client->node, &cdev->client_list);
    496
    497	up_write(&cdev->client_lock);
    498
    499	stream_open(inode, filp);
    500	return 0;
    501}
    502
    503static int ssam_cdev_device_release(struct inode *inode, struct file *filp)
    504{
    505	struct ssam_cdev_client *client = filp->private_data;
    506
    507	/* Force-unregister all remaining notifiers of this client. */
    508	ssam_cdev_notifier_unregister_all(client);
    509
    510	/* Detach client. */
    511	down_write(&client->cdev->client_lock);
    512	list_del(&client->node);
    513	up_write(&client->cdev->client_lock);
    514
    515	/* Free client. */
    516	mutex_destroy(&client->write_lock);
    517	mutex_destroy(&client->read_lock);
    518
    519	mutex_destroy(&client->notifier_lock);
    520
    521	ssam_cdev_put(client->cdev);
    522	vfree(client);
    523
    524	return 0;
    525}
    526
    527static long __ssam_cdev_device_ioctl(struct ssam_cdev_client *client, unsigned int cmd,
    528				     unsigned long arg)
    529{
    530	lockdep_assert_held_read(&client->cdev->lock);
    531
    532	switch (cmd) {
    533	case SSAM_CDEV_REQUEST:
    534		return ssam_cdev_request(client, (struct ssam_cdev_request __user *)arg);
    535
    536	case SSAM_CDEV_NOTIF_REGISTER:
    537		return ssam_cdev_notif_register(client,
    538						(struct ssam_cdev_notifier_desc __user *)arg);
    539
    540	case SSAM_CDEV_NOTIF_UNREGISTER:
    541		return ssam_cdev_notif_unregister(client,
    542						  (struct ssam_cdev_notifier_desc __user *)arg);
    543
    544	case SSAM_CDEV_EVENT_ENABLE:
    545		return ssam_cdev_event_enable(client, (struct ssam_cdev_event_desc __user *)arg);
    546
    547	case SSAM_CDEV_EVENT_DISABLE:
    548		return ssam_cdev_event_disable(client, (struct ssam_cdev_event_desc __user *)arg);
    549
    550	default:
    551		return -ENOTTY;
    552	}
    553}
    554
    555static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    556{
    557	struct ssam_cdev_client *client = file->private_data;
    558	long status;
    559
    560	/* Ensure that controller is valid for as long as we need it. */
    561	if (down_read_killable(&client->cdev->lock))
    562		return -ERESTARTSYS;
    563
    564	if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags)) {
    565		up_read(&client->cdev->lock);
    566		return -ENODEV;
    567	}
    568
    569	status = __ssam_cdev_device_ioctl(client, cmd, arg);
    570
    571	up_read(&client->cdev->lock);
    572	return status;
    573}
    574
    575static ssize_t ssam_cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offs)
    576{
    577	struct ssam_cdev_client *client = file->private_data;
    578	struct ssam_cdev *cdev = client->cdev;
    579	unsigned int copied;
    580	int status = 0;
    581
    582	if (down_read_killable(&cdev->lock))
    583		return -ERESTARTSYS;
    584
    585	/* Make sure we're not shut down. */
    586	if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
    587		up_read(&cdev->lock);
    588		return -ENODEV;
    589	}
    590
    591	do {
    592		/* Check availability, wait if necessary. */
    593		if (kfifo_is_empty(&client->buffer)) {
    594			up_read(&cdev->lock);
    595
    596			if (file->f_flags & O_NONBLOCK)
    597				return -EAGAIN;
    598
    599			status = wait_event_interruptible(client->waitq,
    600							  !kfifo_is_empty(&client->buffer) ||
    601							  test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT,
    602								   &cdev->flags));
    603			if (status < 0)
    604				return status;
    605
    606			if (down_read_killable(&cdev->lock))
    607				return -ERESTARTSYS;
    608
    609			/* Need to check that we're not shut down again. */
    610			if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
    611				up_read(&cdev->lock);
    612				return -ENODEV;
    613			}
    614		}
    615
    616		/* Try to read from FIFO. */
    617		if (mutex_lock_interruptible(&client->read_lock)) {
    618			up_read(&cdev->lock);
    619			return -ERESTARTSYS;
    620		}
    621
    622		status = kfifo_to_user(&client->buffer, buf, count, &copied);
    623		mutex_unlock(&client->read_lock);
    624
    625		if (status < 0) {
    626			up_read(&cdev->lock);
    627			return status;
    628		}
    629
    630		/* We might not have gotten anything, check this here. */
    631		if (copied == 0 && (file->f_flags & O_NONBLOCK)) {
    632			up_read(&cdev->lock);
    633			return -EAGAIN;
    634		}
    635	} while (copied == 0);
    636
    637	up_read(&cdev->lock);
    638	return copied;
    639}
    640
    641static __poll_t ssam_cdev_poll(struct file *file, struct poll_table_struct *pt)
    642{
    643	struct ssam_cdev_client *client = file->private_data;
    644	__poll_t events = 0;
    645
    646	if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags))
    647		return EPOLLHUP | EPOLLERR;
    648
    649	poll_wait(file, &client->waitq, pt);
    650
    651	if (!kfifo_is_empty(&client->buffer))
    652		events |= EPOLLIN | EPOLLRDNORM;
    653
    654	return events;
    655}
    656
    657static int ssam_cdev_fasync(int fd, struct file *file, int on)
    658{
    659	struct ssam_cdev_client *client = file->private_data;
    660
    661	return fasync_helper(fd, file, on, &client->fasync);
    662}
    663
    664static const struct file_operations ssam_controller_fops = {
    665	.owner          = THIS_MODULE,
    666	.open           = ssam_cdev_device_open,
    667	.release        = ssam_cdev_device_release,
    668	.read           = ssam_cdev_read,
    669	.poll           = ssam_cdev_poll,
    670	.fasync         = ssam_cdev_fasync,
    671	.unlocked_ioctl = ssam_cdev_device_ioctl,
    672	.compat_ioctl   = ssam_cdev_device_ioctl,
    673	.llseek         = no_llseek,
    674};
    675
    676
    677/* -- Device and driver setup ----------------------------------------------- */
    678
    679static int ssam_dbg_device_probe(struct platform_device *pdev)
    680{
    681	struct ssam_controller *ctrl;
    682	struct ssam_cdev *cdev;
    683	int status;
    684
    685	ctrl = ssam_client_bind(&pdev->dev);
    686	if (IS_ERR(ctrl))
    687		return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
    688
    689	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
    690	if (!cdev)
    691		return -ENOMEM;
    692
    693	kref_init(&cdev->kref);
    694	init_rwsem(&cdev->lock);
    695	cdev->ctrl = ctrl;
    696	cdev->dev = &pdev->dev;
    697
    698	cdev->mdev.parent   = &pdev->dev;
    699	cdev->mdev.minor    = MISC_DYNAMIC_MINOR;
    700	cdev->mdev.name     = "surface_aggregator";
    701	cdev->mdev.nodename = "surface/aggregator";
    702	cdev->mdev.fops     = &ssam_controller_fops;
    703
    704	init_rwsem(&cdev->client_lock);
    705	INIT_LIST_HEAD(&cdev->client_list);
    706
    707	status = misc_register(&cdev->mdev);
    708	if (status) {
    709		kfree(cdev);
    710		return status;
    711	}
    712
    713	platform_set_drvdata(pdev, cdev);
    714	return 0;
    715}
    716
    717static int ssam_dbg_device_remove(struct platform_device *pdev)
    718{
    719	struct ssam_cdev *cdev = platform_get_drvdata(pdev);
    720	struct ssam_cdev_client *client;
    721
    722	/*
    723	 * Mark device as shut-down. Prevent new clients from being added and
    724	 * new operations from being executed.
    725	 */
    726	set_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags);
    727
    728	down_write(&cdev->client_lock);
    729
    730	/* Remove all notifiers registered by us. */
    731	list_for_each_entry(client, &cdev->client_list, node) {
    732		ssam_cdev_notifier_unregister_all(client);
    733	}
    734
    735	/* Wake up async clients. */
    736	list_for_each_entry(client, &cdev->client_list, node) {
    737		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
    738	}
    739
    740	/* Wake up blocking clients. */
    741	list_for_each_entry(client, &cdev->client_list, node) {
    742		wake_up_interruptible(&client->waitq);
    743	}
    744
    745	up_write(&cdev->client_lock);
    746
    747	/*
    748	 * The controller is only guaranteed to be valid for as long as the
    749	 * driver is bound. Remove controller so that any lingering open files
    750	 * cannot access it any more after we're gone.
    751	 */
    752	down_write(&cdev->lock);
    753	cdev->ctrl = NULL;
    754	cdev->dev = NULL;
    755	up_write(&cdev->lock);
    756
    757	misc_deregister(&cdev->mdev);
    758
    759	ssam_cdev_put(cdev);
    760	return 0;
    761}
    762
    763static struct platform_device *ssam_cdev_device;
    764
    765static struct platform_driver ssam_cdev_driver = {
    766	.probe = ssam_dbg_device_probe,
    767	.remove = ssam_dbg_device_remove,
    768	.driver = {
    769		.name = SSAM_CDEV_DEVICE_NAME,
    770		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
    771	},
    772};
    773
    774static int __init ssam_debug_init(void)
    775{
    776	int status;
    777
    778	ssam_cdev_device = platform_device_alloc(SSAM_CDEV_DEVICE_NAME,
    779						 PLATFORM_DEVID_NONE);
    780	if (!ssam_cdev_device)
    781		return -ENOMEM;
    782
    783	status = platform_device_add(ssam_cdev_device);
    784	if (status)
    785		goto err_device;
    786
    787	status = platform_driver_register(&ssam_cdev_driver);
    788	if (status)
    789		goto err_driver;
    790
    791	return 0;
    792
    793err_driver:
    794	platform_device_del(ssam_cdev_device);
    795err_device:
    796	platform_device_put(ssam_cdev_device);
    797	return status;
    798}
    799module_init(ssam_debug_init);
    800
    801static void __exit ssam_debug_exit(void)
    802{
    803	platform_driver_unregister(&ssam_cdev_driver);
    804	platform_device_unregister(ssam_cdev_device);
    805}
    806module_exit(ssam_debug_exit);
    807
    808MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
    809MODULE_DESCRIPTION("User-space interface for Surface System Aggregator Module");
    810MODULE_LICENSE("GPL");