[alsa-devel] [PATCH v2] MIDI driver for Behringer BCD2000 USB device

Daniel Mack daniel at zonque.org
Wed Feb 5 23:32:23 CET 2014


On 02/05/2014 09:10 PM, Mario Kicherer wrote:
> This patch adds initial support for the Behringer BCD2000 USB DJ controller.
> At the moment, only the MIDI part of the device is working, i.e. knobs,
> buttons and LEDs.
> 
> I also plan to add support for the audio part, but I assume that this will
> require more effort than the rather simple MIDI interface. Progress can be
> tracked at https://github.com/anyc/snd-usb-bcd2000.
> 
> Changes since v1:
>         - fixed the various code style issues, thanks to Daniel Mack and
>           checkpatch.pl.

Hmm, we're getting there :) But I still see:

total: 571 errors, 6 warnings, 580 lines checked

Mostly due to:

ERROR: DOS line endings

Did you send the patch using git send-email?

Also, please have a quick look at Documentation/CodingStyle.

> --- /dev/null
> +++ b/sound/usb/bcd2000/bcd2000.c
> @@ -0,0 +1,553 @@
> +/*
> + * Behringer BCD2000 driver
> + *
> + *   Copyright (C) 2014 Mario Kicherer (dev at kicherer.org)
> + *
> + *   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.
> + *
> + *   This program is distributed in the hope that it will be useful,
> + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *   GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/usb/audio.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>

As long as you don't need them, skip the pcm includes.

> +#include <sound/rawmidi.h>
> +#include "../usbaudio.h"
> +#include "../midi.h"
> +
> +#define DEVICE_NAME "Behringer BCD2000"
> +#define DEVICE_SHORTNAME "bcd2000"
> +
> +#define PREFIX DEVICE_SHORTNAME ": "
> +#define BUFSIZE 64
> +
> +static struct usb_device_id id_table[] = {
> +	{ USB_DEVICE(0x1397, 0x00bd) },
> +	{ },
> +};
> +
> +static int debug;

Hmm, what about the idea of moving the hex_dump function into an own
helper? See below ...

> +module_param(debug, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(debug, "enable debug output (default: 0)");
> +
> +static unsigned char device_cmd_prefix[] = {0x03, 0x00};
> +
> +static unsigned char bcd2000_init_sequence[] = {
> +	0x07, 0x00, 0x00, 0x00, 0x78, 0x48, 0x1c, 0x81,
> +	0xc4, 0x00, 0x00, 0x00, 0x5e, 0x53, 0x4a, 0xf7,
> +	0x18, 0xfa, 0x11, 0xff, 0x6c, 0xf3, 0x90, 0xff,
> +	0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
> +	0x18, 0xfa, 0x11, 0xff, 0x14, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0xf2, 0x34, 0x4a, 0xf7,
> +	0x18, 0xfa, 0x11, 0xff};

'};' should be in a new line.


> +struct bcd2000 {
> +	struct usb_device *dev;
> +	struct snd_card *card;
> +	struct usb_interface *intf;
> +	int card_index;
> +	struct snd_pcm *pcm;

Unused?

> +	struct list_head midi_list;
> +	spinlock_t lock;

Unused?

> +	struct mutex mutex;

That one's also kinda unused (see below)


...

> +static DEFINE_MUTEX(devices_mutex);
> +static unsigned int devices_used;
> +static struct usb_driver bcd2000_driver;
> +
> +
> +
> +
> +

Kill excessive newlines here.

> +static void bcd2000_midi_handle_input(struct bcd2000 *bcd2k,
> +					const unsigned char *buf, int len)
> +{
> +	unsigned char length, cmd, buf_offset, tocopy;
> +
> +	if (!bcd2k->midi_receive_substream)
> +		return;
> +
> +	if (debug) {
> +		print_hex_dump(KERN_DEBUG, "received from device: ",
> +				DUMP_PREFIX_NONE, 16, 1, buf, len, false);
> +	}

You could add something like this:

#ifdef CONFIG_SND_DEBUG
static void bcd2000_midi_hex_dump(const char *prefix,
				  const unsigned char *buf, int len)
{
	print_hex_dump(KERN_DEBUG, prefix,
		       DUMP_PREFIX_NONE, 16, 1, buf, len, false);
}
#else
static inline void bcd2000_midi_hex_dump(const char *prefix,
				  const unsigned char *buf, int len) {}
#endif

And then just call the function from various places. That would safe you
the module parameter, the condition check, and an indentation level.

> +
> +	if (len < 2)
> +		return;
> +
> +	/*
> +	 * The BCD2000 sends MIDI commands with 2 or 3 bytes length. In case of
> +	 * 2 bytes, the command is the same as before.
> +	 *
> +	 * Packet structure: mm nn oo (pp)
> +	 *	mm: payload length
> +	 *	nn: MIDI command or note
> +	 *	oo: note or velocity
> +	 *	pp: velocity
> +	 */
> +
> +	length = buf[0];
> +
> +	/* ignore packets without payload */
> +	if (length == 0)
> +		return;
> +
> +	/*
> +	 * The MIDI packets the BCD2000 sends can be arbitrarily truncated or
> +	 * concatenated. Therefore, this loop accumulates the bytes from the
> +	 * input buffer until a full valid MIDI packet is in the MIDI command
> +	 * buffer "midi_cmd_buf".
> +	 */
> +
> +	buf_offset = 1;
> +	while (buf_offset <= length) {
> +		cmd = buf[buf_offset];
> +
> +		if (bcd2k->midi_cmd_offset == 0 && cmd != 0x90 && cmd != 0xb0) {
> +			/*
> +			 * this is a 2-byte midi packet -> reuse command byte
> +			 * from last midi packet
> +			 */
> +			bcd2k->midi_cmd_offset = 1;
> +		}
> +
> +		/* determine the number of bytes we want to copy this time */
> +		tocopy = min(3 - bcd2k->midi_cmd_offset,
> +					length - (buf_offset - 1));
> +
> +		/* safety check */
> +		if (bcd2k->midi_cmd_offset + tocopy < BUFSIZE &&
> +				buf_offset + tocopy < len) {
> +			memcpy(&bcd2k->midi_cmd_buf[bcd2k->midi_cmd_offset],
> +				&buf[buf_offset], tocopy);
> +		} else {
> +			snd_printk(KERN_ERR PREFIX "access violation in %s\n",
> +					__func__);

You want to drop the entire packet here, right?

> +		}
> +
> +		bcd2k->midi_cmd_offset += tocopy;
> +		buf_offset += tocopy;
> +
> +		/* is our MIDI packet complete? */
> +		if (bcd2k->midi_cmd_offset == 3) {
> +			if (debug) {
> +				print_hex_dump(KERN_DEBUG,
> +						"sending to userspace: ",
> +						DUMP_PREFIX_NONE, 16, 1,
> +						bcd2k->midi_cmd_buf,
> +						bcd2k->midi_cmd_offset, false);
> +			}

{} braces are not needed when a block only holds one instruction. See
Documentation/CodingStyle.


> +static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream)
> +{
> +	struct bcd2000 *bcd2k = substream->rmidi->private_data;

Newline here, and elsewhere in your code, after variable declarations.

> +	if (bcd2k->midi_out_active) {
> +		usb_free_urb(bcd2k->midi_out_urb);
> +		bcd2k->midi_out_active = 0;
> +	}
> +	return 0;
> +}
> +
> +static void bcd2000_midi_send(struct bcd2000 *bcd2k,
> +				struct snd_rawmidi_substream *substream)
> +{
> +	int len, ret;
> +
> +	BUILD_BUG_ON(sizeof(device_cmd_prefix) >= BUFSIZE);
> +	/* copy the "set LED" command bytes */
> +	memcpy(bcd2k->midi_out_buf, device_cmd_prefix,
> +		sizeof(device_cmd_prefix));
> +
> +	/*
> +	 * get MIDI packet and leave space for command prefix
> +	 * and payload length
> +	 */
> +	len = snd_rawmidi_transmit(substream,
> +				bcd2k->midi_out_buf + 3, BUFSIZE - 3);
> +
> +	if (len <= 0)
> +		return;
> +
> +	/* set payload length */
> +	bcd2k->midi_out_buf[2] = len;
> +	bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE;
> +
> +	if (debug) {
> +		print_hex_dump(KERN_DEBUG, "sending to device: ",
> +				DUMP_PREFIX_NONE, 16, 1,
> +				bcd2k->midi_out_buf, len+3, false);
> +	}
> +
> +	/* send packet to the BCD2000 */
> +	ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC);

This function is not called from atomic context, so GFP_KERNEL is ok.

> +	if (ret < 0) {
> +		dev_err(&bcd2k->dev->dev, PREFIX
> +			"%s (%p): usb_submit_urb() failed"
> +			"ret=%d, len=%d\n", __func__, substream, ret, len);
> +	} else {
> +		bcd2k->midi_out_active = 1;
> +	}

See above.

> +}
> +
> +static void bcd2000_midi_output_done(struct urb *urb)
> +{
> +	struct bcd2000 *bcd2k = urb->context;
> +
> +	bcd2k->midi_out_active = 0;
> +	if (urb->status != 0) {
> +		dev_err(&urb->dev->dev, PREFIX "urb status: %d\n", urb->status);
> +		return;
> +	}
> +
> +	if (!bcd2k->midi_out_substream)
> +		return;
> +
> +	bcd2000_midi_send(bcd2k, bcd2k->midi_out_substream);
> +}
> +

...

> +static void bcd2000_close_midi(struct bcd2000 *bcd2k)
> +{
> +	usb_free_urb(bcd2k->midi_out_urb);
> +	usb_free_urb(bcd2k->midi_in_urb);
> +}
> +
> +
> +
> +
> +

Too many newlines :)

> +/*
> + * general part
> + */
> +
> +static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
> +						struct usb_interface *interface)
> +{
> +	struct usb_interface *intf;
> +
> +	mutex_lock(&bcd2k->mutex);
> +	intf = bcd2k->intf;
> +	bcd2k->intf = NULL;
> +	mutex_unlock(&bcd2k->mutex);

What are you trying to lock here, and against what? Without another call
site, this mutex is futile.


Thanks,
Daniel



More information about the Alsa-devel mailing list