205 lines
4.4 KiB
C
205 lines
4.4 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|