/*
* f_midi.c -- USB MIDI class function driver
*
* Copyright (C) 2006 Thumtronics Pty Ltd.
* Developed for Thumtronics by Grey Innovation
* Ben Williamson <ben.williamson@greyinnovation.com>
*
* Rewritten for the composite framework
* Copyright (C) 2011 Daniel Mack <zonque@gmail.com>
*
* Based on drivers/usb/gadget/f_audio.c,
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
*
* and drivers/usb/gadget/midi.c,
* Copyright (C) 2006 Thumtronics Pty Ltd.
* Ben Williamson <ben.williamson@greyinnovation.com>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/kfifo.h>
#include <linux/spinlock.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
#include "u_f.h"
#include "u_midi.h"
MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
static const char f_midi_shortname[] = "f_midi";
static const char f_midi_longname[] = "MIDI Gadget";
/*
* We can only handle 16 cables on one single endpoint, as cable numbers are
* stored in 4-bit fields. And as the interface currently only holds one
* single endpoint, this is the maximum number of ports we can allow.
*/
#define MAX_PORTS 16
/* MIDI message states */
enum {
STATE_INITIAL = 0, /* pseudo state */
STATE_1PARAM,
STATE_2PARAM_1,
STATE_2PARAM_2,
STATE_SYSEX_0,
STATE_SYSEX_1,
STATE_SYSEX_2,
STATE_REAL_TIME,
STATE_FINISHED, /* pseudo state */
};
/*
* This is a gadget, and the IN/OUT naming is from the host's perspective.
* USB -> OUT endpoint -> rawmidi
* USB <- IN endpoint <- rawmidi
*/
struct gmidi_in_port {
struct snd_rawmidi_substream *substream;
int active;
uint8_t cable;
uint8_t state;
uint8_t data[2];
};
struct f_midi {
struct usb_function func;
struct usb_gadget *gadget;
struct usb_ep *in_ep, *out_ep;
struct snd_card *card;
struct snd_rawmidi *rmidi;
u8 ms_id;
struct snd_rawmidi_substream *out_substream[MAX_PORTS];
unsigned long out_triggered;
struct tasklet_struct tasklet;
unsigned int in_ports;
unsigned int out_ports;
int index;
char *id;
unsigned int buflen, qlen;
/* This fifo is used as a buffer ring for pre-allocated IN usb_requests */
DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
spinlock_t transmit_lock;
unsigned int in_last_port;
unsigned char free_ref;
struct gmidi_in_port in_ports_array[/* in_ports */];
};
static inline struct f_midi *func_to_midi(struct usb_function *f)
{
return container_of(f, struct f_midi, func);
}
static void f_midi_transmit(struct f_midi *midi);
static void f_midi_rmidi_free(struct snd_rawmidi *rmidi);
DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
DECLARE_USB_MID