hpioctl.c 15.1 KB
Newer Older
1 2
/*******************************************************************************
    AudioScience HPI driver
3 4 5
    Common Linux HPI ioctl and module probe/remove functions

    Copyright (C) 1997-2014  AudioScience Inc. <support@audioscience.com>
6 7 8 9 10 11 12 13 14 15 16 17 18 19

    This program is free software; you can redistribute it and/or modify
    it under the terms of version 2 of the GNU General Public License as
    published by the Free Software Foundation;

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

*******************************************************************************/
#define SOURCEFILE_NAME "hpioctl.c"

#include "hpi_internal.h"
20
#include "hpi_version.h"
21 22 23 24
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
25
#include "hpicmn.h"
26 27

#include <linux/fs.h>
28
#include <linux/interrupt.h>
29 30
#include <linux/slab.h>
#include <linux/moduleparam.h>
31
#include <linux/uaccess.h>
32
#include <linux/pci.h>
33
#include <linux/stringify.h>
34
#include <linux/module.h>
35
#include <linux/vmalloc.h>
36 37 38 39 40 41 42 43 44 45 46 47 48 49

#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
MODULE_FIRMWARE("asihpi/dsp6200.bin");
MODULE_FIRMWARE("asihpi/dsp6205.bin");
MODULE_FIRMWARE("asihpi/dsp6400.bin");
MODULE_FIRMWARE("asihpi/dsp6600.bin");
MODULE_FIRMWARE("asihpi/dsp8700.bin");
MODULE_FIRMWARE("asihpi/dsp8900.bin");
#endif

static int prealloc_stream_buf;
module_param(prealloc_stream_buf, int, S_IRUGO);
MODULE_PARM_DESC(prealloc_stream_buf,
50
	"Preallocate size for per-adapter stream buffer");
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

/* Allow the debug level to be changed after module load.
 E.g.   echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
*/
module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");

/* List of adapters found */
static struct hpi_adapter adapters[HPI_MAX_ADAPTERS];

/* Wrapper function to HPI_Message to enable dumping of the
   message and response types.
*/
static void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr,
	struct file *file)
{
67
	if ((phm->adapter_index >= HPI_MAX_ADAPTERS)
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
		&& (phm->object != HPI_OBJ_SUBSYSTEM))
		phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
	else
		hpi_send_recv_ex(phm, phr, file);
}

/* This is called from hpifunc.c functions, called by ALSA
 * (or other kernel process) In this case there is no file descriptor
 * available for the message cache code
 */
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr)
{
	hpi_send_recv_f(phm, phr, HOWNER_KERNEL);
}

EXPORT_SYMBOL(hpi_send_recv);
/* for radio-asihpi */

int asihpi_hpi_release(struct file *file)
{
	struct hpi_message hm;
	struct hpi_response hr;

/* HPI_DEBUG_LOG(INFO,"hpi_release file %p, pid %d\n", file, current->pid); */
	/* close the subsystem just in case the application forgot to. */
	hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
		HPI_SUBSYS_CLOSE);
	hpi_send_recv_ex(&hm, &hr, file);
	return 0;
}

long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct hpi_ioctl_linux __user *phpi_ioctl_data;
	void __user *puhm;
	void __user *puhr;
	union hpi_message_buffer_v1 *hm;
	union hpi_response_buffer_v1 *hr;
	u16 res_max_size;
	u32 uncopied_bytes;
	int err = 0;

	if (cmd != HPI_IOCTL_LINUX)
		return -EINVAL;

	hm = kmalloc(sizeof(*hm), GFP_KERNEL);
	hr = kmalloc(sizeof(*hr), GFP_KERNEL);
	if (!hm || !hr) {
		err = -ENOMEM;
		goto out;
	}

	phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;

	/* Read the message and response pointers from user space.  */
123 124
	if (get_user(puhm, &phpi_ioctl_data->phm)
		|| get_user(puhr, &phpi_ioctl_data->phr)) {
125 126 127
		err = -EFAULT;
		goto out;
	}
128 129

	/* Now read the message size and data from user space.  */
130 131 132 133
	if (get_user(hm->h.size, (u16 __user *)puhm)) {
		err = -EFAULT;
		goto out;
	}
134 135 136
	if (hm->h.size > sizeof(*hm))
		hm->h.size = sizeof(*hm);

137
	/* printk(KERN_INFO "message size %d\n", hm->h.wSize); */
138 139 140 141 142 143 144 145

	uncopied_bytes = copy_from_user(hm, puhm, hm->h.size);
	if (uncopied_bytes) {
		HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
		err = -EFAULT;
		goto out;
	}

146 147 148 149
	if (get_user(res_max_size, (u16 __user *)puhr)) {
		err = -EFAULT;
		goto out;
	}
150 151 152 153 154 155 156
	/* printk(KERN_INFO "user response size %d\n", res_max_size); */
	if (res_max_size < sizeof(struct hpi_response_header)) {
		HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size);
		err = -EFAULT;
		goto out;
	}

157 158
	res_max_size = min_t(size_t, res_max_size, sizeof(*hr));

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
	switch (hm->h.function) {
	case HPI_SUBSYS_CREATE_ADAPTER:
	case HPI_ADAPTER_DELETE:
		/* Application must not use these functions! */
		hr->h.size = sizeof(hr->h);
		hr->h.error = HPI_ERROR_INVALID_OPERATION;
		hr->h.function = hm->h.function;
		uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
		if (uncopied_bytes)
			err = -EFAULT;
		else
			err = 0;
		goto out;
	}

174
	hr->h.size = res_max_size;
175
	if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
176
		hpi_send_recv_f(&hm->m0, &hr->r0, file);
177 178 179 180 181
	} else {
		u16 __user *ptr = NULL;
		u32 size = 0;
		/* -1=no data 0=read from user mem, 1=write to user mem */
		int wrflag = -1;
182
		struct hpi_adapter *pa = NULL;
183

184
		if (hm->h.adapter_index < ARRAY_SIZE(adapters))
185
			pa = &adapters[hm->h.adapter_index];
186

187
		if (!pa || !pa->adapter || !pa->adapter->type) {
188 189
			hpi_init_response(&hr->r0, hm->h.object,
				hm->h.function, HPI_ERROR_BAD_ADAPTER_NUMBER);
190 191 192 193 194 195 196 197 198 199

			uncopied_bytes =
				copy_to_user(puhr, hr, sizeof(hr->h));
			if (uncopied_bytes)
				err = -EFAULT;
			else
				err = 0;
			goto out;
		}

200
		if (mutex_lock_interruptible(&pa->mutex)) {
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
			err = -EINTR;
			goto out;
		}

		/* Dig out any pointers embedded in the message.  */
		switch (hm->h.function) {
		case HPI_OSTREAM_WRITE:
		case HPI_ISTREAM_READ:{
				/* Yes, sparse, this is correct. */
				ptr = (u16 __user *)hm->m0.u.d.u.data.pb_data;
				size = hm->m0.u.d.u.data.data_size;

				/* Allocate buffer according to application request.
				   ?Is it better to alloc/free for the duration
				   of the transaction?
				 */
				if (pa->buffer_size < size) {
					HPI_DEBUG_LOG(DEBUG,
219
						"Realloc adapter %d stream "
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
						"buffer from %zd to %d\n",
						hm->h.adapter_index,
						pa->buffer_size, size);
					if (pa->p_buffer) {
						pa->buffer_size = 0;
						vfree(pa->p_buffer);
					}
					pa->p_buffer = vmalloc(size);
					if (pa->p_buffer)
						pa->buffer_size = size;
					else {
						HPI_DEBUG_LOG(ERROR,
							"HPI could not allocate "
							"stream buffer size %d\n",
							size);

236
						mutex_unlock(&pa->mutex);
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
						err = -EINVAL;
						goto out;
					}
				}

				hm->m0.u.d.u.data.pb_data = pa->p_buffer;
				if (hm->h.function == HPI_ISTREAM_READ)
					/* from card, WRITE to user mem */
					wrflag = 1;
				else
					wrflag = 0;
				break;
			}

		default:
			size = 0;
			break;
		}

		if (size && (wrflag == 0)) {
			uncopied_bytes =
				copy_from_user(pa->p_buffer, ptr, size);
			if (uncopied_bytes)
				HPI_DEBUG_LOG(WARNING,
261
					"Missed %d of %d "
262 263 264 265 266 267 268 269 270 271 272
					"bytes from user\n", uncopied_bytes,
					size);
		}

		hpi_send_recv_f(&hm->m0, &hr->r0, file);

		if (size && (wrflag == 1)) {
			uncopied_bytes =
				copy_to_user(ptr, pa->p_buffer, size);
			if (uncopied_bytes)
				HPI_DEBUG_LOG(WARNING,
273
					"Missed %d of %d " "bytes to user\n",
274 275 276
					uncopied_bytes, size);
		}

277
		mutex_unlock(&pa->mutex);
278 279 280 281 282 283 284 285 286 287 288 289 290 291
	}

	/* on return response size must be set */
	/*printk(KERN_INFO "response size %d\n", hr->h.wSize); */

	if (!hr->h.size) {
		HPI_DEBUG_LOG(ERROR, "response zero size\n");
		err = -EFAULT;
		goto out;
	}

	if (hr->h.size > res_max_size) {
		HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
			res_max_size);
292 293 294
		hr->h.error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
		hr->h.specific_error = hr->h.size;
		hr->h.size = sizeof(hr->h);
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	}

	uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
	if (uncopied_bytes) {
		HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
		err = -EFAULT;
		goto out;
	}

out:
	kfree(hm);
	kfree(hr);
	return err;
}

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
static int asihpi_irq_count;

static irqreturn_t asihpi_isr(int irq, void *dev_id)
{
	struct hpi_adapter *a = dev_id;
	int handled;

	if (!a->adapter->irq_query_and_clear) {
		pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type,
			a->adapter->index);
		return IRQ_NONE;
	}

	handled = a->adapter->irq_query_and_clear(a->adapter, 0);

	if (!handled)
		return IRQ_NONE;

	asihpi_irq_count++;
	/* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n",
	   asihpi_irq_count, a->adapter->type, a->adapter->index); */

	if (a->interrupt_callback)
		a->interrupt_callback(a);

	return IRQ_HANDLED;
}

338 339
int asihpi_adapter_probe(struct pci_dev *pci_dev,
			 const struct pci_device_id *pci_id)
340
{
341
	int idx, nm, low_latency_mode = 0, irq_supported = 0;
342
	int adapter_index;
343 344 345 346 347 348 349 350
	unsigned int memlen;
	struct hpi_message hm;
	struct hpi_response hr;
	struct hpi_adapter adapter;
	struct hpi_pci pci;

	memset(&adapter, 0, sizeof(adapter));

351 352 353
	dev_printk(KERN_DEBUG, &pci_dev->dev,
		"probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor,
		pci_dev->device, pci_dev->subsystem_vendor,
354 355
		pci_dev->subsystem_device, pci_dev->devfn);

356
	if (pci_enable_device(pci_dev) < 0) {
357
		dev_err(&pci_dev->dev,
358 359 360 361 362 363
			"pci_enable_device failed, disabling device\n");
		return -EIO;
	}

	pci_set_master(pci_dev);	/* also sets latency timer if < 16 */

364 365 366 367 368
	hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
		HPI_SUBSYS_CREATE_ADAPTER);
	hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER,
		HPI_ERROR_PROCESSING_MESSAGE);

369
	hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
370 371 372 373

	nm = HPI_MAX_ADAPTER_MEM_SPACES;

	for (idx = 0; idx < nm; idx++) {
374 375
		HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
			&pci_dev->resource[idx]);
376 377 378

		if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
			memlen = pci_resource_len(pci_dev, idx);
379
			pci.ap_mem_base[idx] =
380 381
				ioremap(pci_resource_start(pci_dev, idx),
				memlen);
382
			if (!pci.ap_mem_base[idx]) {
383 384 385 386 387 388 389 390
				HPI_DEBUG_LOG(ERROR,
					"ioremap failed, aborting\n");
				/* unmap previously mapped pci mem space */
				goto err;
			}
		}
	}

391
	pci.pci_dev = pci_dev;
392 393 394 395 396 397 398 399
	hm.u.s.resource.bus_type = HPI_BUS_PCI;
	hm.u.s.resource.r.pci = &pci;

	/* call CreateAdapterObject on the relevant hpi module */
	hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
	if (hr.error)
		goto err;

400 401 402
	adapter_index = hr.u.s.adapter_index;
	adapter.adapter = hpi_find_adapter(adapter_index);

403 404 405 406 407 408 409 410 411 412 413
	if (prealloc_stream_buf) {
		adapter.p_buffer = vmalloc(prealloc_stream_buf);
		if (!adapter.p_buffer) {
			HPI_DEBUG_LOG(ERROR,
				"HPI could not allocate "
				"kernel buffer size %d\n",
				prealloc_stream_buf);
			goto err;
		}
	}

414 415
	hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
		HPI_ADAPTER_OPEN);
416
	hm.adapter_index = adapter.adapter->index;
417
	hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
418

419 420 421 422 423 424 425 426 427 428 429
	if (hr.error) {
		HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n");
		goto err;
	}

	/* Check if current mode == Low Latency mode */
	hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
		HPI_ADAPTER_GET_MODE);
	hm.adapter_index = adapter.adapter->index;
	hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);

430 431
	if (!hr.error
		&& hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY)
432
		low_latency_mode = 1;
433 434 435 436
	else
		dev_info(&pci_dev->dev,
			"Adapter at index %d is not in low latency mode\n",
			adapter.adapter->index);
437 438 439 440 441 442 443 444 445 446 447 448 449 450

	/* Check if IRQs are supported */
	hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
		HPI_ADAPTER_GET_PROPERTY);
	hm.adapter_index = adapter.adapter->index;
	hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ;
	hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
	if (hr.error || !hr.u.ax.property_get.parameter1) {
		dev_info(&pci_dev->dev,
			"IRQs not supported by adapter at index %d\n",
			adapter.adapter->index);
	} else {
		irq_supported = 1;
	}
451 452 453 454

	/* WARNING can't init mutex in 'adapter'
	 * and then copy it to adapters[] ?!?!
	 */
455 456 457
	adapters[adapter_index] = adapter;
	mutex_init(&adapters[adapter_index].mutex);
	pci_set_drvdata(pci_dev, &adapters[adapter_index]);
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
	if (low_latency_mode && irq_supported) {
		if (!adapter.adapter->irq_query_and_clear) {
			dev_err(&pci_dev->dev,
				"no IRQ handler for adapter %d, aborting\n",
				adapter.adapter->index);
			goto err;
		}

		/* Disable IRQ generation on DSP side by setting the rate to 0 */
		hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
			HPI_ADAPTER_SET_PROPERTY);
		hm.adapter_index = adapter.adapter->index;
		hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
		hm.u.ax.property_set.parameter1 = 0;
		hm.u.ax.property_set.parameter2 = 0;
		hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
		if (hr.error) {
			HPI_DEBUG_LOG(ERROR,
				"HPI_ADAPTER_GET_MODE failed, aborting\n");
			goto err;
		}

		/* Note: request_irq calls asihpi_isr here */
		if (request_irq(pci_dev->irq, asihpi_isr, IRQF_SHARED,
				"asihpi", &adapters[adapter_index])) {
			dev_err(&pci_dev->dev, "request_irq(%d) failed\n",
				pci_dev->irq);
			goto err;
		}

		adapters[adapter_index].interrupt_mode = 1;

		dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq);
		adapters[adapter_index].irq = pci_dev->irq;
	} else {
		dev_info(&pci_dev->dev, "using polled mode\n");
	}

497 498
	dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n",
		 adapter.adapter->type, adapter_index);
499 500 501 502 503

	return 0;

err:
	for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
504 505 506
		if (pci.ap_mem_base[idx]) {
			iounmap(pci.ap_mem_base[idx]);
			pci.ap_mem_base[idx] = NULL;
507 508 509 510 511 512 513 514 515 516 517 518
		}
	}

	if (adapter.p_buffer) {
		adapter.buffer_size = 0;
		vfree(adapter.p_buffer);
	}

	HPI_DEBUG_LOG(ERROR, "adapter_probe failed\n");
	return -ENODEV;
}

519
void asihpi_adapter_remove(struct pci_dev *pci_dev)
520 521 522 523 524
{
	int idx;
	struct hpi_message hm;
	struct hpi_response hr;
	struct hpi_adapter *pa;
525 526
	struct hpi_pci pci;

527
	pa = pci_get_drvdata(pci_dev);
528
	pci = pa->adapter->pci;
529

530 531 532 533 534 535 536 537 538
	/* Disable IRQ generation on DSP side */
	hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
		HPI_ADAPTER_SET_PROPERTY);
	hm.adapter_index = pa->adapter->index;
	hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
	hm.u.ax.property_set.parameter1 = 0;
	hm.u.ax.property_set.parameter2 = 0;
	hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);

539 540
	hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
		HPI_ADAPTER_DELETE);
541
	hm.adapter_index = pa->adapter->index;
542 543 544
	hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);

	/* unmap PCI memory space, mapped during device init. */
545 546
	for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; ++idx)
		iounmap(pci.ap_mem_base[idx]);
547

548 549 550
	if (pa->irq)
		free_irq(pa->irq, pa);

551
	vfree(pa->p_buffer);
552

553
	if (1)
554 555 556 557 558
		dev_info(&pci_dev->dev,
			 "remove %04x:%04x,%04x:%04x,%04x, HPI index %d\n",
			 pci_dev->vendor, pci_dev->device,
			 pci_dev->subsystem_vendor, pci_dev->subsystem_device,
			 pci_dev->devfn, pa->adapter->index);
559 560

	memset(pa, 0, sizeof(*pa));
561 562 563 564 565 566 567 568 569
}

void __init asihpi_init(void)
{
	struct hpi_message hm;
	struct hpi_response hr;

	memset(adapters, 0, sizeof(adapters));

570
	printk(KERN_INFO "ASIHPI driver " HPI_VER_STRING "\n");
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585

	hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
		HPI_SUBSYS_DRIVER_LOAD);
	hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
}

void asihpi_exit(void)
{
	struct hpi_message hm;
	struct hpi_response hr;

	hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
		HPI_SUBSYS_DRIVER_UNLOAD);
	hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
}