Пример #1
0
void usb_conn_packet_send(struct usb_conn_t *conn, struct http_packet_t *pkt)
{
	int size_sent = 0;
	const int timeout = 6 * 60 * 60 * 1000; // 6 hours in milliseconds
	int num_timeouts = 0;
	size_t sent = 0;
	size_t pending = pkt->filled_size;
	while (pending > 0) {
		int to_send = (int)pending;

		NOTE("USB: want to send %d bytes", to_send);
		int status = libusb_bulk_transfer(conn->parent->printer,
		                                  conn->interface->endpoint_out,
		                                  pkt->buffer + sent, to_send,
		                                  &size_sent, timeout);
		if (status == LIBUSB_ERROR_TIMEOUT) {
			NOTE("USB: send timed out, retrying");

			if (num_timeouts++ > PRINTER_CRASH_TIMEOUT)
				ERR_AND_EXIT("Usb send fully timed out");

			// Sleep for tenth of a second
			struct timespec sleep_dur;
			sleep_dur.tv_sec = 0;
			sleep_dur.tv_nsec = 100000000;
			nanosleep(&sleep_dur, NULL);
			continue;
		}

		if (status == LIBUSB_ERROR_NO_DEVICE)
			ERR_AND_EXIT("Printer has been disconnected");
		if (status < 0)
			ERR_AND_EXIT("USB: send failed with status %s",
				libusb_error_name(status));
		if (size_sent < 0)
			ERR_AND_EXIT("Unexpected negative size_sent");

		pending -= (size_t) size_sent;
		sent += (size_t) size_sent;
		NOTE("USB: sent %d bytes", size_sent);
	}
	NOTE("USB: sent %d bytes in total", sent);
}
Пример #2
0
struct usb_sock_t *usb_open()
{
	struct usb_sock_t *usb = calloc(1, sizeof *usb);
	int status = 1;
	status = libusb_init(&usb->context);
	if (status < 0) {
		ERR("libusb init failed with error: %s",
			libusb_error_name(status));
		goto error_usbinit;
	}

	libusb_device **device_list = NULL;
	ssize_t device_count = libusb_get_device_list(usb->context, &device_list);
	if (device_count < 0) {
		ERR("failed to get list of usb devices");
		goto error;
	}


	// Discover device and count interfaces ==---------------------------==
	int selected_config = -1;
	unsigned int selected_ipp_interface_count = 0;
	int auto_pick = !(g_options.vendor_id ||
	                  g_options.product_id ||
	                  g_options.serial_num);

	libusb_device *printer_device = NULL;
	for (ssize_t i = 0; i < device_count; i++) {
		libusb_device *candidate = device_list[i];
		struct libusb_device_descriptor desc;
		libusb_get_device_descriptor(candidate, &desc);

		if (!is_our_device(candidate, desc))
			continue;

		for (uint8_t config_num = 0;
		     config_num < desc.bNumConfigurations;
		     config_num++) {
			struct libusb_config_descriptor *config = NULL;
			status = libusb_get_config_descriptor(candidate,
			                                      config_num,
			                                      &config);
			if (status < 0)
				ERR_AND_EXIT("USB: didn't get config desc %s",
					libusb_error_name(status));

			int interface_count = count_ippoverusb_interfaces(config);
			libusb_free_config_descriptor(config);
			if (interface_count >= 2) {
				selected_config = config_num;
				selected_ipp_interface_count = (unsigned) interface_count;
				printer_device = candidate;
				goto found_device;
			}

			// CONFTEST: Two or more interfaces are required
			if (interface_count == 1) {
				CONF("usb device has only one ipp interface "
				    "in violation of standard");
				goto error;
			}

			if (!auto_pick) {
				ERR_AND_EXIT("No ipp-usb interfaces found");
			}
		}
	}
found_device:

	if (printer_device == NULL) {
		if (!auto_pick) {
			ERR("No printer found by that vid, pid, serial");
		} else {
			ERR("No IPP over USB printer found");
		}
		goto error;
	}


	// Open the printer ==-----------------------------------------------==
	status = libusb_open(printer_device, &usb->printer);
	if (status != 0) {
		ERR("failed to open device");
		goto error;
	}


	// Open every IPP-USB interface ==-----------------------------------==
	usb->num_interfaces = selected_ipp_interface_count;
	usb->interfaces = calloc(usb->num_interfaces,
	                         sizeof(*usb->interfaces));
	if (usb->interfaces == NULL) {
		ERR("Failed to alloc interfaces");
		goto error;
	}

	struct libusb_config_descriptor *config = NULL;
	status = libusb_get_config_descriptor(printer_device,
	                                      (uint8_t)selected_config,
	                                      &config);
	if (status != 0 || config == NULL) {
		ERR("Failed to aquire config descriptor");
		goto error;
	}

	unsigned int interfs = selected_ipp_interface_count;
	for (uint8_t interf_num = 0;
	     interf_num < config->bNumInterfaces;
	     interf_num++) {
	
		const struct libusb_interface *interf = NULL;
		interf = &config->interface[interf_num];
		for (int alt_num = 0;
		     alt_num < interf->num_altsetting;
		     alt_num++) {
	
			const struct libusb_interface_descriptor *alt = NULL;
			alt = &interf->altsetting[alt_num];

			// Skip non-IPP-USB interfaces
			if (!is_ippusb_interface(alt))
				continue;

			interfs--;

			struct usb_interface *uf = usb->interfaces + interfs;
			uf->interface_number = interf_num;
			uf->libusb_interface_index = alt->bInterfaceNumber;
			uf->interface_alt = alt_num;

			// Store interface's two endpoints
			for (int end_i = 0; end_i < alt->bNumEndpoints;
			     end_i++) {
				const struct libusb_endpoint_descriptor *end;
				end = &alt->endpoint[end_i];

				usb->max_packet_size = end->wMaxPacketSize;

				// High bit set means endpoint
				// is an INPUT or IN endpoint.
				uint8_t address = end->bEndpointAddress;
				if (address & 0x80)
					uf->endpoint_in = address;
				else
					uf->endpoint_out = address;
			}

			break;
		}
	}
	libusb_free_config_descriptor(config);
	libusb_free_device_list(device_list, 1);


	// Pour interfaces into pool ==--------------------------------------==
	usb->num_avail = usb->num_interfaces;
	usb->interface_pool = calloc(usb->num_avail,
	                             sizeof(*usb->interface_pool));
	if (usb->interface_pool == NULL) {
		ERR("Failed to alloc interface pool");
		goto error;
	}
	for (uint32_t i = 0; i < usb->num_avail; i++) {
		usb->interface_pool[i] = i;
	}


	// Stale lock
	int status_lock = sem_init(&usb->num_staled_lock, 0, 1);
	if (status_lock != 0) {
		ERR("Failed to create num_staled lock");
		goto error;
	}

	// Pool management lock
	status_lock = sem_init(&usb->pool_manage_lock, 0, 1);
	if (status_lock != 0) {
		ERR("Failed to create high priority pool lock");
		goto error;
	}
	// High priority lock
	status_lock = sem_init(&usb->pool_high_priority_lock, 0, 1);
	if (status_lock != 0) {
		ERR("Failed to create low priority pool lock");
		goto error;
	}
	// Low priority lock
	status_lock = sem_init(&usb->pool_low_priority_lock,
	                       0, usb->num_avail - 1);
	if (status_lock != 0) {
		ERR("Failed to create high priority pool lock");
		goto error;
	}

	return usb;

error:
	if (device_list != NULL)
		libusb_free_device_list(device_list, 1);
error_usbinit:
	if (usb != NULL) {
		if (usb->context != NULL)
			libusb_exit(usb->context);
		if (usb->interfaces != NULL)
			free(usb->interfaces);
		if (usb->interface_pool != NULL)
			free(usb->interface_pool);
		free(usb);
	}
	return NULL;
}
Пример #3
0
struct http_packet_t *usb_conn_packet_get(struct usb_conn_t *conn, struct http_message_t *msg)
{
	if (msg->is_completed)
		return NULL;

	struct http_packet_t *pkt = packet_new(msg);
	if (pkt == NULL) {
		ERR("failed to create packet for incoming usb message");
		goto cleanup;
	}

	// File packet
	const int timeout = 6 * 60 * 60 * 1000; // 6 hours in milliseconds
	size_t read_size_ulong = packet_pending_bytes(pkt);
	if (read_size_ulong == 0)
		return pkt;

	int times_staled = 0;
	while (read_size_ulong > 0 && !msg->is_completed) {
		if (read_size_ulong >= INT_MAX)
			goto cleanup;
		int read_size = (int)read_size_ulong;

		// Ensure read_size is multiple of usb packets
		read_size += (512 - (read_size % 512)) % 512;

		// Expand buffer if needed
		if (pkt->buffer_capacity < pkt->filled_size + read_size_ulong)
			if (packet_expand(pkt) < 0)
				ERR_AND_EXIT("Failed to ensure room for usb pkt");

		NOTE("USB: Getting %d bytes of %d", read_size, pkt->expected_size);
		int gotten_size = 0;
		int status = libusb_bulk_transfer(
		                      conn->parent->printer,
		                      conn->interface->endpoint_in,
		                      pkt->buffer + pkt->filled_size,
		                      read_size,
		                      &gotten_size, timeout);

		if (status == LIBUSB_ERROR_NO_DEVICE)
			ERR_AND_EXIT("Printer has been disconnected");

		if (status != 0 && status != LIBUSB_ERROR_TIMEOUT) {
			ERR("bulk xfer failed with error code %d", status);
			ERR("tried reading %d bytes", read_size);
			goto cleanup;
		}

		if (gotten_size < 0)
			ERR_AND_EXIT("Negative read size unexpected");

		if (gotten_size > 0) {
			times_staled = 0;
			usb_conn_mark_moving(conn);
		} else {
			times_staled++;
			if (times_staled > CONN_STALE_THRESHHOLD) {
				usb_conn_mark_staled(conn);

				if (usb_all_conns_staled(conn->parent) &&
				    times_staled > PRINTER_CRASH_TIMEOUT) {
					ERR("USB timedout, dropping data");
					goto cleanup;
				}

				if (pkt->filled_size > 0)
					NOTE("Packet so far \n%.*s\n",
						pkt->filled_size,
						pkt->buffer);
			}

			// Sleep for tenth of a second
			struct timespec sleep_dur;
			sleep_dur.tv_sec = 0;
			sleep_dur.tv_nsec = 100000000;
			nanosleep(&sleep_dur, NULL);
		}

		NOTE("USB: Got %d bytes", gotten_size);
		packet_mark_received(pkt, (size_t)gotten_size);
		read_size_ulong = packet_pending_bytes(pkt);
	}
	NOTE("USB: Received %d bytes of %d with type %d",
			pkt->filled_size, pkt->expected_size, msg->type);

	return pkt;

cleanup:
	if (pkt != NULL)
		packet_free(pkt);
	return NULL;
}
Пример #4
0
struct usb_conn_t *usb_conn_aquire(struct usb_sock_t *usb,
                                   int is_high_priority)
{
	int used_high_priority = 0;
	if (is_high_priority) {
		// We first take the high priority lock.
		sem_wait(&usb->pool_high_priority_lock);
		used_high_priority = 1;

		// We can then check if a low priority
		// interface is available.
		if (!sem_trywait(&usb->pool_low_priority_lock)) {
			// If a low priority is avail we take that one
			// then release the high priority interface.
			// Otherwise we use the high priority interface.
			used_high_priority = 0;
			sem_post(&usb->pool_high_priority_lock);
		}
	} else {
		sem_wait(&usb->pool_low_priority_lock);
	}

	struct usb_conn_t *conn = calloc(1, sizeof(*conn));
	if (conn == NULL) {
		ERR("failed to aloc space for usb connection");
		return NULL;
	}

	sem_wait(&usb->pool_manage_lock);
	{
		conn->parent = usb;
		conn->is_high_priority = used_high_priority;

		usb->num_taken++;
		uint32_t slot = --usb->num_avail;

		conn->interface_index = usb->interface_pool[slot];
		conn->interface = usb->interfaces + conn->interface_index;
		struct usb_interface *uf = conn->interface;

		// Make kernel release interface
		if (libusb_kernel_driver_active(usb->printer,
		                            uf->libusb_interface_index) == 1) {
			// Only linux supports this
			// other platforms will fail
			// thus we ignore the error code
			// it either works or it does not
			libusb_detach_kernel_driver(usb->printer,
			                           uf->libusb_interface_index);
		}

		// Claim the whole interface
		int status = 0;
		do {
			// Spinlock-like
			// Libusb does not offer a blocking call
			// so we're left with a spinlock
			status = libusb_claim_interface(
				usb->printer, uf->libusb_interface_index);
			switch (status) {
			case LIBUSB_ERROR_NOT_FOUND:
				ERR_AND_EXIT("USB Interface did not exist");
			case LIBUSB_ERROR_NO_DEVICE:
				ERR_AND_EXIT("Printer was removed");
			default:
				break;
			}
		} while (status != 0);

		// Select the IPP-USB alt setting of the interface
		libusb_set_interface_alt_setting(usb->printer,
		                                 uf->libusb_interface_index,
		                                 uf->interface_alt);
	}
	sem_post(&usb->pool_manage_lock);
	return conn;
}
Пример #5
0
static void *service_connection(void *arg_void)
{
	struct service_thread_param *arg =
		(struct service_thread_param *)arg_void;

	// clasify priority
	while (!arg->tcp->is_closed) {
		struct usb_conn_t *usb = NULL;
		struct http_message_t *server_msg = NULL;
		struct http_message_t *client_msg = NULL;

		// Client's request
		NOTE("Client msg starting");
		client_msg = http_message_new();
		if (client_msg == NULL) {
			ERR("Failed to create message");
			break;
		}

		while (!client_msg->is_completed) {
			struct http_packet_t *pkt;
			pkt = tcp_packet_get(arg->tcp, client_msg);
			if (pkt == NULL) {
				if (arg->tcp->is_closed) {
					NOTE("Client closed connection\n");
					goto cleanup_subconn;
				}
				ERR_AND_EXIT("Got null packet from tcp");
			}
			if (usb == NULL) {
				usb = usb_conn_aquire(arg->usb_sock, 1);
				if (usb == NULL) {
					ERR("Failed to aquire usb interface");
					packet_free(pkt);
					goto cleanup_subconn;
				}
				NOTE("Interface #%d: aquired usb conn",
						usb->interface_index);
			}

			//NOTE("%.*s", (int)pkt->filled_size, pkt->buffer);
			usb_conn_packet_send(usb, pkt);
			packet_free(pkt);
		}
		message_free(client_msg);
		client_msg = NULL;
		NOTE("Interface #%d: Client msg completed\n",
				usb->interface_index);


		// Server's responce
		NOTE("Interface #%d: Server msg starting",
				usb->interface_index);
		server_msg = http_message_new();
		if (server_msg == NULL) {
			ERR("Failed to create message");
			goto cleanup_subconn;
		}
		while (!server_msg->is_completed) {
			struct http_packet_t *pkt;
			pkt = usb_conn_packet_get(usb, server_msg);
			if (pkt == NULL)
				break;

			NOTE("Pkt from usb: \n%.*s",
					(int)pkt->filled_size, pkt->buffer);
			tcp_packet_send(arg->tcp, pkt);
			packet_free(pkt);
			NOTE("Interface #%d: Server pkt done",
					usb->interface_index);
		}
		NOTE("Interface #%d: Server msg completed\n",
				usb->interface_index);

cleanup_subconn:
		if (client_msg != NULL)
			message_free(client_msg);
		if (server_msg != NULL)
			message_free(server_msg);
		if (usb != NULL)
			usb_conn_release(usb);
	}



	tcp_conn_close(arg->tcp);
	free(arg);
	return NULL;
}