diff options
Diffstat (limited to 'sound/usb/6fire/comm.c')
| -rw-r--r-- | sound/usb/6fire/comm.c | 204 | 
1 files changed, 204 insertions, 0 deletions
diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c new file mode 100644 index 00000000000..161215d78d9 --- /dev/null +++ b/sound/usb/6fire/comm.c @@ -0,0 +1,204 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Device communications + * + * Author:	Torsten Schenk <torsten.schenk@zoho.com> + * Created:	Jan 01, 2011 + * Copyright:	(C) Torsten Schenk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "comm.h" +#include "chip.h" +#include "midi.h" + +enum { +	COMM_EP = 1, +	COMM_FPGA_EP = 2 +}; + +static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb, +		u8 *buffer, void *context, void(*handler)(struct urb *urb)) +{ +	usb_init_urb(urb); +	urb->transfer_buffer = buffer; +	urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP); +	urb->complete = handler; +	urb->context = context; +	urb->interval = 1; +	urb->dev = rt->chip->dev; +} + +static void usb6fire_comm_receiver_handler(struct urb *urb) +{ +	struct comm_runtime *rt = urb->context; +	struct midi_runtime *midi_rt = rt->chip->midi; + +	if (!urb->status) { +		if (rt->receiver_buffer[0] == 0x10) /* midi in event */ +			if (midi_rt) +				midi_rt->in_received(midi_rt, +						rt->receiver_buffer + 2, +						rt->receiver_buffer[1]); +	} + +	if (!rt->chip->shutdown) { +		urb->status = 0; +		urb->actual_length = 0; +		if (usb_submit_urb(urb, GFP_ATOMIC) < 0) +			dev_warn(&urb->dev->dev, +					"comm data receiver aborted.\n"); +	} +} + +static void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request, +		u8 reg, u8 vl, u8 vh) +{ +	buffer[0] = 0x01; +	buffer[2] = request; +	buffer[3] = id; +	switch (request) { +	case 0x02: +		buffer[1] = 0x05; /* length (starting at buffer[2]) */ +		buffer[4] = reg; +		buffer[5] = vl; +		buffer[6] = vh; +		break; + +	case 0x12: +		buffer[1] = 0x0b; /* length (starting at buffer[2]) */ +		buffer[4] = 0x00; +		buffer[5] = 0x18; +		buffer[6] = 0x05; +		buffer[7] = 0x00; +		buffer[8] = 0x01; +		buffer[9] = 0x00; +		buffer[10] = 0x9e; +		buffer[11] = reg; +		buffer[12] = vl; +		break; + +	case 0x20: +	case 0x21: +	case 0x22: +		buffer[1] = 0x04; +		buffer[4] = reg; +		buffer[5] = vl; +		break; +	} +} + +static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev) +{ +	int ret; +	int actual_len; + +	ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP), +			buffer, buffer[1] + 2, &actual_len, HZ); +	if (ret < 0) +		return ret; +	else if (actual_len != buffer[1] + 2) +		return -EIO; +	return 0; +} + +static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request, +		u8 reg, u8 value) +{ +	u8 *buffer; +	int ret; + +	/* 13: maximum length of message */ +	buffer = kmalloc(13, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00); +	ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev); + +	kfree(buffer); +	return ret; +} + +static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request, +		u8 reg, u8 vl, u8 vh) +{ +	u8 *buffer; +	int ret; + +	/* 13: maximum length of message */ +	buffer = kmalloc(13, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh); +	ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev); + +	kfree(buffer); +	return ret; +} + +int usb6fire_comm_init(struct sfire_chip *chip) +{ +	struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime), +			GFP_KERNEL); +	struct urb *urb; +	int ret; + +	if (!rt) +		return -ENOMEM; + +	rt->receiver_buffer = kzalloc(COMM_RECEIVER_BUFSIZE, GFP_KERNEL); +	if (!rt->receiver_buffer) { +		kfree(rt); +		return -ENOMEM; +	} + +	urb = &rt->receiver; +	rt->serial = 1; +	rt->chip = chip; +	usb_init_urb(urb); +	rt->init_urb = usb6fire_comm_init_urb; +	rt->write8 = usb6fire_comm_write8; +	rt->write16 = usb6fire_comm_write16; + +	/* submit an urb that receives communication data from device */ +	urb->transfer_buffer = rt->receiver_buffer; +	urb->transfer_buffer_length = COMM_RECEIVER_BUFSIZE; +	urb->pipe = usb_rcvintpipe(chip->dev, COMM_EP); +	urb->dev = chip->dev; +	urb->complete = usb6fire_comm_receiver_handler; +	urb->context = rt; +	urb->interval = 1; +	ret = usb_submit_urb(urb, GFP_KERNEL); +	if (ret < 0) { +		kfree(rt->receiver_buffer); +		kfree(rt); +		dev_err(&chip->dev->dev, "cannot create comm data receiver."); +		return ret; +	} +	chip->comm = rt; +	return 0; +} + +void usb6fire_comm_abort(struct sfire_chip *chip) +{ +	struct comm_runtime *rt = chip->comm; + +	if (rt) +		usb_poison_urb(&rt->receiver); +} + +void usb6fire_comm_destroy(struct sfire_chip *chip) +{ +	struct comm_runtime *rt = chip->comm; + +	kfree(rt->receiver_buffer); +	kfree(rt); +	chip->comm = NULL; +}  | 
