예제 #1
0
파일: drec.c 프로젝트: jvesely/helenos
/**
 * Start fragment based recording.
 * @param rec Recording helper structure.
 * @param f PCM format
 */
static void record_fragment(record_t *rec, pcm_format_t f)
{
	assert(rec);
	assert(rec->device);
	int ret = audio_pcm_register_event_callback(rec->device,
	    device_event_callback, rec);
	if (ret != EOK) {
		printf("Failed to register for events: %s.\n", str_error(ret));
		return;
	}
	rec->buffer.position = rec->buffer.base;
	printf("Recording: %dHz, %s, %d channel(s).\n", f.sampling_rate,
	    pcm_sample_format_str(f.sample_format), f.channels);
	const unsigned frames =
		pcm_format_size_to_frames(rec->buffer.size / BUFFER_PARTS, &f);
	ret = audio_pcm_start_capture_fragment(rec->device,
	    frames, f.channels, f.sampling_rate, f.sample_format);
	if (ret != EOK) {
		printf("Failed to start recording: %s.\n", str_error(ret));
		return;
	}

	getchar();
	printf("\n");
	audio_pcm_stop_capture(rec->device);
	/* XXX Control returns even before we can be sure callbacks finished */
	printf("Delay before playback termination\n");
	async_usleep(1000000);
	printf("Terminate playback\n");
}
예제 #2
0
/** Main function of software period fibrill
 *
 *  Just calls poll() in the nic->poll_period period
 *
 *  @param  data The NIC structure pointer
 *
 *  @return 0, never reached
 */
static int period_fibril_fun(void *data)
{
	nic_t *nic = data;
	struct sw_poll_info *info = &nic->sw_poll_info;
	while (true) {
		fibril_rwlock_read_lock(&nic->main_lock);
		int run = info->run;
		int running = info->running;
		struct timeval remaining = nic->poll_period;
		fibril_rwlock_read_unlock(&nic->main_lock);

		if (!running) {
			remaining.tv_sec = 5;
			remaining.tv_usec = 0;
		}

		/* Wait the period (keep attention to overflows) */
		while (!timeval_nonpositive(remaining)) {
			suseconds_t wait = 0;
			if (remaining.tv_sec > 0) {
				time_t wait_sec = remaining.tv_sec;
				/* wait maximaly 5 seconds to get reasonable reaction time
				 * when period is reset
				 */
				if (wait_sec > 5)
					wait_sec = 5;

				wait = (suseconds_t) wait_sec * 1000000;

				remaining.tv_sec -= wait_sec;
			} else {
				wait = remaining.tv_usec;

				if (wait > 5 * 1000000) {
					wait = 5 * 1000000;
				}

				remaining.tv_usec -= wait;
			}
			async_usleep(wait);
			
			/* Check if the period was not reset */
			if (info->run != run)
				break;
		}
		
		/* Provide polling if the period finished */
		fibril_rwlock_read_lock(&nic->main_lock);
		if (info->running && info->run == run) {
			nic->on_poll_request(nic);
		}
		fibril_rwlock_read_unlock(&nic->main_lock);
	}
	return 0;
}
예제 #3
0
/** Simulate plugging and surprise unplugging.
 *
 * @param arg Parent device structure (ddf_dev_t *).
 * @return Always EOK.
 */
static int plug_unplug(void *arg)
{
	test2_t *test2 = (test2_t *) arg;
	ddf_fun_t *fun_a;
	int rc;

	async_usleep(1000);

	(void) register_fun_verbose(test2->dev, "child driven by the same task",
	    "child", "virtual&test2", 10, &test2->child);
	(void) register_fun_verbose(test2->dev, "child driven by test1",
	    "test1", "virtual&test1", 10, &test2->test1);

	fun_a = ddf_fun_create(test2->dev, fun_exposed, "a");
	if (fun_a == NULL) {
		ddf_msg(LVL_ERROR, "Failed creating function 'a'.");
		return ENOMEM;
	}

	rc = ddf_fun_bind(fun_a);
	if (rc != EOK) {
		ddf_msg(LVL_ERROR, "Failed binding function 'a'.");
		return rc;
	}

	ddf_fun_add_to_category(fun_a, "virtual");
	test2->fun_a = fun_a;

	async_usleep(10000000);

	ddf_msg(LVL_NOTE, "Unbinding function test1.");
	ddf_fun_unbind(test2->test1);
	async_usleep(1000000);
	ddf_msg(LVL_NOTE, "Unbinding function child.");
	ddf_fun_unbind(test2->child);

	return EOK;
}
예제 #4
0
/** Initialize UHCI hc hw resources.
 *
 * @param[in] instance UHCI structure to use.
 * For magic values see UHCI Design Guide
 */
void hc_init_hw(const hc_t *instance)
{
	assert(instance);
	uhci_regs_t *registers = instance->registers;

	/* Reset everything, who knows what touched it before us */
	pio_write_16(&registers->usbcmd, UHCI_CMD_GLOBAL_RESET);
	async_usleep(50000); /* 50ms according to USB spec(root hub reset) */
	pio_write_16(&registers->usbcmd, 0);

	/* Reset hc, all states and counters. Hope that hw is not broken */
	pio_write_16(&registers->usbcmd, UHCI_CMD_HCRESET);
	do { async_usleep(10); }
	while ((pio_read_16(&registers->usbcmd) & UHCI_CMD_HCRESET) != 0);

	/* Set frame to exactly 1ms */
	pio_write_8(&registers->sofmod, 64);

	/* Set frame list pointer */
	const uint32_t pa = addr_to_phys(instance->frame_list);
	pio_write_32(&registers->flbaseadd, pa);

	if (instance->hw_interrupts) {
		/* Enable all interrupts, but resume interrupt */
		pio_write_16(&instance->registers->usbintr,
		    UHCI_INTR_ALLOW_INTERRUPTS);
	}

	const uint16_t cmd = pio_read_16(&registers->usbcmd);
	if (cmd != 0)
		usb_log_warning("Previous command value: %x.\n", cmd);

	/* Start the hc with large(64B) packet FSBR */
	pio_write_16(&registers->usbcmd,
	    UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET | UHCI_CMD_CONFIGURE);
}
예제 #5
0
/** Polling function, emulates interrupts.
 *
 * @param[in] arg UHCI hc structure to use.
 * @return EOK (should never return)
 */
int hc_interrupt_emulator(void* arg)
{
	usb_log_debug("Started interrupt emulator.\n");
	hc_t *instance = arg;
	assert(instance);

	while (1) {
		/* Read and clear status register */
		uint16_t status = pio_read_16(&instance->registers->usbsts);
		pio_write_16(&instance->registers->usbsts, status);
		if (status != 0)
			usb_log_debug2("UHCI status: %x.\n", status);
		hc_interrupt(instance, status);
		async_usleep(UHCI_INT_EMULATOR_TIMEOUT);
	}
	return EOK;
}
예제 #6
0
static int transmit_fibril(void *arg)
{
	uint16_t seq_no = 0;

	while (true) {
		fibril_mutex_lock(&done_lock);
		if (done) {
			fibril_mutex_unlock(&done_lock);
			return 0;
		}
		fibril_mutex_unlock(&done_lock);

		(void) ping_send(++seq_no);
		async_usleep(PING_DELAY);
	}

	return 0;
}
예제 #7
0
파일: main.c 프로젝트: jvesely/helenos
/**
 * Callback for removing a device from the driver.
 *
 * @param dev Structure representing the device.
 * @return Error code.
 */
static int usb_hid_device_gone(usb_device_t *dev)
{
	assert(dev);
	usb_hid_dev_t *hid_dev = usb_device_data_get(dev);
	assert(hid_dev);
	unsigned tries = 100;
	/* Wait for fail. */
	while (hid_dev->running && tries--) {
		async_usleep(100000);
	}
	if (hid_dev->running) {
		usb_log_error("Can't remove hid, still running.\n");
		return EBUSY;
	}

	usb_hid_deinit(hid_dev);
	usb_log_debug2("%s destruction complete.\n", usb_device_get_name(dev));
	return EOK;
}
예제 #8
0
파일: hcd.c 프로젝트: jvesely/helenos
/** this is really ugly version of sync usb communication */
ssize_t hcd_send_batch_sync(
    hcd_t *hcd, usb_target_t target, usb_direction_t dir,
    void *data, size_t size, uint64_t setup_data, const char* name)
{
	assert(hcd);
	sync_data_t sd = { .done = 0, .ret = EBUSY, .size = size };

	const int ret = hcd_send_batch(hcd, target, dir, data, size, setup_data,
	    dir == USB_DIRECTION_IN ? transfer_in_cb : NULL,
	    dir == USB_DIRECTION_OUT ? transfer_out_cb : NULL, &sd, name);
	if (ret != EOK)
		return ret;

	while (!sd.done) {
		async_usleep(1000);
	}

	if (sd.ret == EOK)
		return sd.size;
	return sd.ret;
}
예제 #9
0
/**
 * Remove all attached devices
 * @param usb_dev generic usb device information
 * @return error code
 */
int usb_hub_device_gone(usb_device_t *usb_dev)
{
	assert(usb_dev);
	usb_hub_dev_t *hub = usb_dev->driver_data;
	assert(hub);
	unsigned tries = 10;
	while (hub->running) {
		async_usleep(100000);
		if (!tries--) {
			usb_log_error("Can't remove hub, still running.\n");
			return EINPROGRESS;
		}
	}

	assert(!hub->running);

	for (size_t port = 0; port < hub->port_count; ++port) {
		if (hub->ports[port].attached_device.fun) {
			const int ret =
			    usb_hub_port_fini(&hub->ports[port], hub);
			if (ret != EOK)
				return ret;
		}
	}
	free(hub->ports);

	const int ret = ddf_fun_unbind(hub->hub_fun);
	if (ret != EOK) {
		usb_log_error("Failed to unbind '%s' function: %s.\n",
		   HUB_FNC_NAME, str_error(ret));
		return ret;
	}
	ddf_fun_destroy(hub->hub_fun);

	usb_log_info("USB hub driver, stopped and cleaned.\n");
	return EOK;
}
예제 #10
0
/** Polling fibril.
 *
 * @param arg Pointer to polling_data_t.
 * @return Always EOK.
 */
static int polling_fibril(void *arg)
{
	assert(arg);
	const polling_data_t *data = arg;
	/* Helper to reduce typing. */
	const usb_device_auto_polling_t *params = &data->auto_polling;

	usb_pipe_t *pipe
	    = &data->dev->pipes[data->pipe_index].pipe;

	if (params->debug > 0) {
		const usb_endpoint_mapping_t *mapping
		    = &data->dev->pipes[data->pipe_index];
		usb_log_debug("Poll%p: started polling of `%s' - " \
		    "interface %d (%s,%d,%d), %zuB/%zu.\n",
		    data, data->dev->ddf_dev->name,
		    (int) mapping->interface->interface_number,
		    usb_str_class(mapping->interface->interface_class),
		    (int) mapping->interface->interface_subclass,
		    (int) mapping->interface->interface_protocol,
		    data->request_size, pipe->max_packet_size);
	}

	usb_pipe_start_long_transfer(pipe);
	size_t failed_attempts = 0;
	while (failed_attempts <= params->max_failures) {
		size_t actual_size;
		const int rc = usb_pipe_read(pipe, data->buffer,
		    data->request_size, &actual_size);

		if (params->debug > 1) {
			if (rc == EOK) {
				usb_log_debug(
				    "Poll%p: received: '%s' (%zuB).\n",
				    data,
				    usb_debug_str_buffer(data->buffer,
				        actual_size, 16),
				    actual_size);
			} else {
				usb_log_debug(
				    "Poll%p: polling failed: %s.\n",
				    data, str_error(rc));
			}
		}

		/* If the pipe stalled, we can try to reset the stall. */
		if ((rc == ESTALL) && (params->auto_clear_halt)) {
			/*
			 * We ignore error here as this is usually a futile
			 * attempt anyway.
			 */
			usb_request_clear_endpoint_halt(
			    &data->dev->ctrl_pipe, pipe->endpoint_no);
		}

		if (rc != EOK) {
			++failed_attempts;
			const bool cont = (params->on_error == NULL) ? true :
			    params->on_error(data->dev, rc, params->arg);
			if (!cont) {
				failed_attempts = params->max_failures;
			}
			continue;
		}

		/* We have the data, execute the callback now. */
		assert(params->on_data);
		const bool carry_on = params->on_data(
		    data->dev, data->buffer, actual_size, params->arg);

		if (!carry_on) {
			/* This is user requested abort, erases failures. */
			failed_attempts = 0;
			break;
		}

		/* Reset as something might be only a temporary problem. */
		failed_attempts = 0;

		/* Take a rest before next request. */
		async_usleep(params->delay);
	}

	usb_pipe_end_long_transfer(pipe);

	const bool failed = failed_attempts > 0;

	if (params->on_polling_end != NULL) {
		params->on_polling_end(data->dev, failed, params->arg);
	}

	if (params->debug > 0) {
		if (failed) {
			usb_log_error("Polling of device `%s' terminated: "
			    "recurring failures.\n", data->dev->ddf_dev->name);
		} else {
			usb_log_debug("Polling of device `%s' terminated: "
			    "driver request.\n", data->dev->ddf_dev->name);
		}
	}

	/* Free the allocated memory. */
	free(data->buffer);
	free(data);

	return EOK;
}
예제 #11
0
/** Wrapper for registering attached device to the hub.
 *
 * The @p enable_port function is expected to enable signaling on given
 * port.
 * The argument can have arbitrary meaning and it is not touched at all
 * by this function (it is passed as is to the @p enable_port function).
 *
 * If the @p enable_port fails (i.e. does not return EOK), the device
 * addition is canceled.
 * The return value is then returned (it is good idea to use different
 * error codes than those listed as return codes by this function itself).
 *
 * The @p connection representing connection with host controller does not
 * need to be started.
 * This function duplicates the connection to allow simultaneous calls of
 * this function (i.e. from different fibrils).
 *
 * @param[in] parent Parent device (i.e. the hub device).
 * @param[in] connection Connection to host controller. Must be non-null.
 * @param[in] dev_speed New device speed.
 * @param[in] enable_port Function for enabling signaling through the port the
 *	device is attached to.
 * @param[in] arg Any data argument to @p enable_port.
 * @param[out] assigned_address USB address of the device.
 * @param[in] dev_ops Child device ops. Will use default if not provided.
 * @param[in] new_dev_data Arbitrary pointer to be stored in the child
 *	as @c driver_data. Will allocate and assign usb_hub_attached_device_t
 *	structure if NULL.
 * @param[out] new_fun Storage where pointer to allocated child function
 *	will be written. Must be non-null.
 * @return Error code.
 * @retval EINVAL Either connection or new_fun is a NULL pointer.
 * @retval ENOENT Connection to HC not opened.
 * @retval EADDRNOTAVAIL Failed retrieving free address from host controller.
 * @retval EBUSY Failed reserving default USB address.
 * @retval ENOTCONN Problem connecting to the host controller via USB pipe.
 * @retval ESTALL Problem communication with device (either SET_ADDRESS
 *	request or requests for descriptors when creating match ids).
 */
int usb_hc_new_device_wrapper(ddf_dev_t *parent,
                              usb_hc_connection_t *hc_conn, usb_speed_t dev_speed,
                              int (*enable_port)(void *arg), void *arg, usb_address_t *assigned_address,
                              ddf_dev_ops_t *dev_ops, void *new_dev_data, ddf_fun_t **new_fun)
{
    if ((new_fun == NULL) || (hc_conn == NULL))
        return EINVAL;

    int rc;
    struct timeval start_time;

    rc = gettimeofday(&start_time, NULL);
    if (rc != EOK) {
        return rc;
    }

    /* We are gona do a lot of communication better open it in advance. */
    rc = usb_hc_connection_open(hc_conn);
    if (rc != EOK) {
        return rc;
    }

    /* Request a new address. */
    usb_address_t dev_addr =
        usb_hc_request_address(hc_conn, 0, false, dev_speed);
    if (dev_addr < 0) {
        rc = EADDRNOTAVAIL;
        goto close_connection;
    }

    /* Initialize connection to device. */
    usb_device_connection_t dev_conn;
    rc = usb_device_connection_initialize(
             &dev_conn, hc_conn, USB_ADDRESS_DEFAULT);
    if (rc != EOK) {
        rc = ENOTCONN;
        goto leave_release_free_address;
    }

    /* Initialize control pipe on default address. Don't register yet. */
    usb_pipe_t ctrl_pipe;
    rc = usb_pipe_initialize_default_control(&ctrl_pipe, &dev_conn);
    if (rc != EOK) {
        rc = ENOTCONN;
        goto leave_release_free_address;
    }

    /*
     * The default address request might fail.
     * That means that someone else is already using that address.
     * We will simply wait and try again.
     * (Someone else already wants to add a new device.)
     */
    do {
        rc = usb_hc_request_address(hc_conn, USB_ADDRESS_DEFAULT,
                                    true, dev_speed);
        if (rc == ENOENT) {
            /* Do not overheat the CPU ;-). */
            async_usleep(DEFAULT_ADDRESS_ATTEMPT_DELAY_USEC);
        }
    } while (rc == ENOENT);
    if (rc < 0) {
        goto leave_release_free_address;
    }

    /* Register control pipe on default address. 0 means no interval. */
    rc = usb_pipe_register(&ctrl_pipe, 0);
    if (rc != EOK) {
        rc = ENOTCONN;
        goto leave_release_default_address;
    }

    struct timeval end_time;
    rc = gettimeofday(&end_time, NULL);
    if (rc != EOK) {
        goto leave_release_default_address;
    }

    /* According to the USB spec part 9.1.2 host allows 100ms time for
     * the insertion process to complete. According to 7.1.7.1 this is the
     * time between attach detected and port reset. However, the setup done
     * above might use much of this time so we should only wait to fill
     * up the 100ms quota*/
    const suseconds_t elapsed = tv_sub(&end_time, &start_time);
    if (elapsed < 100000) {
        async_usleep(100000 - elapsed);
    }

    /* Endpoint is registered. We can enable the port and change address. */
    rc = enable_port(arg);
    if (rc != EOK) {
        goto leave_release_default_address;
    }
    /* USB spec 7.1.7.1: The USB System Software guarantees a minimum of
     * 10ms for reset recovery. Device response to any bus transactions
     * addressed to the default device address during the reset recovery
     * time is undefined.
     */
    async_usleep(10000);

    /* Get max_packet_size value. */
    rc = usb_pipe_probe_default_control(&ctrl_pipe);
    if (rc != EOK) {
        rc = ESTALL;
        goto leave_release_default_address;
    }

    rc = usb_request_set_address(&ctrl_pipe, dev_addr);
    if (rc != EOK) {
        rc = ESTALL;
        goto leave_release_default_address;
    }


    /* Register the device with devman. */
    /* FIXME: create device_register that will get opened ctrl pipe. */
    ddf_fun_t *child_fun;
    rc = usb_device_register_child_in_devman(&ctrl_pipe,
            parent, dev_ops, new_dev_data, &child_fun);
    if (rc != EOK) {
        goto leave_release_free_address;
    }

    const usb_hub_attached_device_t new_device = {
        .address = dev_addr,
        .fun = child_fun,
    };


    /* Inform the host controller about the handle. */
    rc = usb_hub_register_device(hc_conn, &new_device);
    if (rc != EOK) {
        /* We know nothing about that data. */
        if (new_dev_data)
            child_fun->driver_data = NULL;
        /* The child function is already created. */
        ddf_fun_destroy(child_fun);
        rc = EDESTADDRREQ;
        goto leave_release_free_address;
    }

    if (assigned_address != NULL) {
        *assigned_address = dev_addr;
    }

    *new_fun = child_fun;

    rc = EOK;
    goto close_connection;

    /*
     * Error handling (like nested exceptions) starts here.
     * Completely ignoring errors here.
     */
leave_release_default_address:
    if (usb_hc_release_address(hc_conn, USB_ADDRESS_DEFAULT) != EOK)
        usb_log_warning("%s: Failed to release defaut address.\n",
                        __FUNCTION__);

leave_release_free_address:
    /* This might be either 0:0 or dev_addr:0 */
    if (usb_pipe_unregister(&ctrl_pipe) != EOK)
        usb_log_warning("%s: Failed to unregister default pipe.\n",
                        __FUNCTION__);

    if (usb_hc_release_address(hc_conn, dev_addr) != EOK)
        usb_log_warning("%s: Failed to release address: %d.\n",
                        __FUNCTION__, dev_addr);

close_connection:
    if (usb_hc_connection_close(hc_conn) != EOK)
        usb_log_warning("%s: Failed to close hc connection.\n",
                        __FUNCTION__);

    return rc;
}
예제 #12
0
/** Debug function, checks consistency of memory structures.
 *
 * @param[in] arg UHCI structure to use.
 * @return EOK (should never return)
 */
int hc_debug_checker(void *arg)
{
	hc_t *instance = arg;
	assert(instance);

#define QH(queue) \
	instance->transfers_##queue.queue_head

	while (1) {
		const uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
		const uint16_t sts = pio_read_16(&instance->registers->usbsts);
		const uint16_t intr =
		    pio_read_16(&instance->registers->usbintr);

		if (((cmd & UHCI_CMD_RUN_STOP) != 1) || (sts != 0)) {
			usb_log_debug2("Command: %X Status: %X Intr: %x\n",
			    cmd, sts, intr);
		}

		const uintptr_t frame_list =
		    pio_read_32(&instance->registers->flbaseadd) & ~0xfff;
		if (frame_list != addr_to_phys(instance->frame_list)) {
			usb_log_debug("Framelist address: %p vs. %p.\n",
			    (void *) frame_list,
			    (void *) addr_to_phys(instance->frame_list));
		}

		int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;

		uintptr_t expected_pa = instance->frame_list[frnum]
		    & LINK_POINTER_ADDRESS_MASK;
		uintptr_t real_pa = addr_to_phys(QH(interrupt));
		if (expected_pa != real_pa) {
			usb_log_debug("Interrupt QH: %p (frame %d) vs. %p.\n",
			    (void *) expected_pa, frnum, (void *) real_pa);
		}

		expected_pa = QH(interrupt)->next & LINK_POINTER_ADDRESS_MASK;
		real_pa = addr_to_phys(QH(control_slow));
		if (expected_pa != real_pa) {
			usb_log_debug("Control Slow QH: %p vs. %p.\n",
			    (void *) expected_pa, (void *) real_pa);
		}

		expected_pa = QH(control_slow)->next & LINK_POINTER_ADDRESS_MASK;
		real_pa = addr_to_phys(QH(control_full));
		if (expected_pa != real_pa) {
			usb_log_debug("Control Full QH: %p vs. %p.\n",
			    (void *) expected_pa, (void *) real_pa);
		}

		expected_pa = QH(control_full)->next & LINK_POINTER_ADDRESS_MASK;
		real_pa = addr_to_phys(QH(bulk_full));
		if (expected_pa != real_pa ) {
			usb_log_debug("Bulk QH: %p vs. %p.\n",
			    (void *) expected_pa, (void *) real_pa);
		}
		async_usleep(UHCI_DEBUGER_TIMEOUT);
	}
	return EOK;
#undef QH
}