File hdjmod_fix_hotplug.patch of Package hdjmod
diff -Naru hdjmod-1.28o/device.c hdjmod-1.28/device.c
--- hdjmod-1.28o/device.c 2009-01-27 15:25:50.000000000 +0100
+++ hdjmod-1.28/device.c 2012-03-13 22:21:56.000000000 +0100
@@ -79,10 +79,9 @@
/* table of devices that work with this driver- look for vendor specific interfaces with
* our VID */
static struct usb_device_id hdj_table [] = {
- { .match_flags = (USB_DEVICE_ID_MATCH_DEVICE),
- .idVendor = (USB_HDJ_VENDOR_ID) },
- { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS),
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC},
+ { .match_flags = (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS),
+ .idVendor = (USB_HDJ_VENDOR_ID),
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, hdj_table);
diff -Naru hdjmod-1.28o/device.c~ hdjmod-1.28/device.c~
--- hdjmod-1.28o/device.c~ 1970-01-01 01:00:00.000000000 +0100
+++ hdjmod-1.28/device.c~ 2012-03-13 22:21:56.000000000 +0100
@@ -0,0 +1,2801 @@
+/*
+*
+* Copyright (c) 2008 Guillemot Corporation S.A.
+*
+* Philip Lukidis plukidis@guillemot.com
+* Alexis Rousseau-Dupuis
+*
+* Partly based on usbaudio by Takashi Iwai
+* Partly based on usb-skeleton.c by Greg Kroah-Hartman
+*
+* 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.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+*/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/version.h> /* For LINUX_VERSION_CODE */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+#include <linux/netlink.h>
+#include <net/sock.h>
+#include <linux/usb.h>
+#if ( LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24) )
+#include <sound/driver.h>
+#endif
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+#include "djdevioctls.h"
+#include "device.h"
+#include "callback.h"
+#include "bulk.h"
+#include "midi.h"
+#include "configuration_manager.h"
+
+/* Taken from usbaudio.c */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */
+static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the Hercules DJ Series adapter.");
+
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the Hercules DJ Series adapter.");
+
+static DECLARE_MUTEX(register_mutex);
+static struct snd_hdj_chip *usb_chip[SNDRV_CARDS];
+
+/* reference count for the socket */
+atomic_t netlink_ref_count = ATOMIC_INIT(0);
+/* socket over which netlink notifications are sent */
+struct sock *nl_sk;
+/* netlink unit used */
+int netlink_unit = NETLINK_UNIT_INVALID_VALUE;
+
+/* table of devices that work with this driver- look for vendor specific interfaces with
+ * our VID */
+static struct usb_device_id hdj_table [] = {
+ { .match_flags = (USB_DEVICE_ID_MATCH_DEVICE),
+ .idVendor = (USB_HDJ_VENDOR_ID) },
+ { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS),
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC},
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, hdj_table);
+
+#ifdef CONFIG_PM
+static int hdj_probe(struct usb_interface *interface, const struct usb_device_id *uid);
+static void hdj_disconnect(struct usb_interface *interface);
+static int hdj_suspend(struct usb_interface *intf, pm_message_t message);
+static int hdj_resume (struct usb_interface *intf);
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) )
+static int hdj_reset_resume(struct usb_interface *intf);
+#endif
+#endif
+
+
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) )
+#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) )
+static int hdj_pre_reset(struct usb_interface *intf);
+static int hdj_post_reset(struct usb_interface *intf);
+#else
+static void hdj_pre_reset(struct usb_interface *intf);
+static void hdj_post_reset(struct usb_interface *intf);
+#endif
+#endif
+
+struct usb_driver hdj_driver = {
+ .name = "hdj_mod",
+ .probe = hdj_probe,
+ .disconnect = hdj_disconnect,
+#ifdef CONFIG_PM
+ .suspend = hdj_suspend,
+ .resume = hdj_resume,
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) )
+ .reset_resume = hdj_reset_resume,
+#endif
+#endif
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) )
+ .pre_reset = hdj_pre_reset,
+ .post_reset = hdj_post_reset,
+#endif
+ .id_table = hdj_table,
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) )
+ .supports_autosuspend = 0,
+#endif
+};
+
+/* used for debugging only!
+void write_to_file(const char* fmt, ...)
+{
+ struct file *dstf;
+ int orgfsuid,orgfsgid;
+ mm_segment_t orgfs;
+ int retval;
+ char buffer[255];
+ int index=0,bufsize;
+ va_list vargs;
+ char *dst = "/root/log";
+
+ orgfsuid=current->fsuid;
+ orgfsgid=current->fsgid;
+ current->fsuid=current->fsgid=0;
+ // save FS register and set FS register to kernel
+ // space, needed for read and write to accept
+ // buffer in kernel space.
+ orgfs=get_fs();
+ set_fs(KERNEL_DS);
+
+ dstf = filp_open(dst, O_WRONLY| O_CREAT | O_APPEND, 0666);
+ if(dstf!=NULL) {
+ // The object must have a write method
+ if (dstf->f_op&&dstf->f_op->write) {
+ memset(buffer,0,sizeof(buffer));
+ va_start(vargs, fmt);
+ vsnprintf(buffer,sizeof(buffer),fmt,vargs);
+ va_end(vargs);
+ index=0;
+ bufsize=strlen(buffer);
+ // Continue writing until error or everything written.
+ while ((index<bufsize)&&
+ ((retval=dstf->f_op->write(dstf,buffer+index,bufsize-index,&dstf->f_pos))>0)
+ )
+ index+=retval;
+ if (index<bufsize)
+ printk("kcp: Write error %d\n",-retval);
+ } else {
+ printk("kcp: %s does not have a write method\n",dst);
+ }
+ retval=filp_close(dstf,NULL);
+ if (retval)
+ printk("kcp: Error %d closing %s\n",-retval,dst);
+ }
+ set_fs(orgfs);
+ current->fsuid=orgfsuid;
+ current->fsgid=orgfsgid;
+}*/
+
+void hdj_kill_chip_urbs(struct snd_hdj_chip *chip)
+{
+ if (chip->ctrl_urb!=NULL) {
+ usb_kill_urb(chip->ctrl_urb);
+ }
+}
+
+/* MARK: PRODCHANGE */
+static int bulk_intf_num_check(struct snd_hdj_chip *chip, int ifnum)
+{
+ if (ifnum == DJ_BULK_IFNUM && (chip->product_code==DJCONSOLE_PRODUCT_CODE ||
+ chip->product_code==DJCONSOLE2_PRODUCT_CODE ||
+ chip->product_code==DJCONSOLERMX_PRODUCT_CODE ||
+ chip->product_code==DJCONTROLSTEEL_PRODUCT_CODE)) {
+ return 1;
+ }
+ return 0;
+}
+
+/* MARK: PRODCHANGE */
+static int midi_intf_num_full_controller_num_check(struct snd_hdj_chip *chip, int ifnum)
+{
+ if ((ifnum == DJ_MIDI_IF_NUM && (chip->product_code==DJCONSOLE_PRODUCT_CODE ||
+ chip->product_code==DJCONSOLE2_PRODUCT_CODE ||
+ chip->product_code==DJCONSOLERMX_PRODUCT_CODE)) ||
+ (ifnum == DJ_MIDI_STEEL_IF_NUM &&
+ chip->product_code==DJCONTROLSTEEL_PRODUCT_CODE)) {
+ return 1;
+ }
+ return 0;
+}
+
+/* MARK: PRODCHANGE */
+static int midimp3_intf_num_full_controller_num_check(struct snd_hdj_chip *chip, int ifnum)
+{
+ if (ifnum==DJ_MP3_IF_NUM && chip->product_code==DJCONTROLLER_PRODUCT_CODE) {
+ return 1;
+ }
+ return 0;
+}
+
+static void proc_fw_version_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ u16 fw_version=-1;
+ int rc;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ /* force a hw read- for those fw update related checks */
+ rc = get_firmware_version(chip,&fw_version,1);
+ snd_iprintf(buffer, "%hx\n",fw_version);
+ if (rc!=0) {
+ printk(KERN_WARNING"%s() get_firmware_version() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_location_id_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct snd_hdjmidi* umidi;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ umidi = midi_from_chip(chip);
+ if (umidi!=NULL) {
+ snd_iprintf(buffer, "%s\n",&umidi->chip->usb_device_path[0]);
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_driver_version_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ snd_iprintf(buffer, "%u\n",driver_version);
+}
+
+static void proc_netlink_unit_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ snd_iprintf(buffer, "%d\n",netlink_unit);
+}
+
+static void proc_drivername_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ if (chip->card->driver[0] != '\0') {
+ snd_iprintf(buffer, "%s\n",chip->card->driver);
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_shortname_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ if (chip->card->shortname[0] != '\0') {
+ snd_iprintf(buffer, "%s\n",chip->card->shortname);
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_longname_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ chip = inc_chip_ref_count(chip_index);
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ if (chip->card->longname[0] != '\0') {
+ snd_iprintf(buffer, "%s\n",chip->card->longname);
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_product_code_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ snd_iprintf(buffer, "%u\n",chip->product_code);
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_jog_lock_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u16 jog_lock;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &jog_lock)) != 1) {
+ break;
+ }
+ if ((rc=set_jogwheel_lock_status(ubulk, jog_lock))!=0) {
+ printk(KERN_WARNING"%s() set_jogwheel_lock_status failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_jog_lock_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 jog_lock;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_jogwheel_lock_status(ubulk,&jog_lock,1,0))==0) {
+ snd_iprintf(buffer, "%hu\n",jog_lock);
+ } else {
+ printk(KERN_WARNING"%s() get_jogwheel_lock_status() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_jog_sens_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u16 jog_sens;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &jog_sens)) != 1) {
+ break;
+ }
+ if ((rc=set_jogwheel_sensitivity(ubulk, jog_sens))!=0) {
+ printk(KERN_WARNING"%s() set_jogwheel_sensitivity failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_jog_sens_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 jog_sens;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_jogwheel_sensitivity(ubulk,&jog_sens,1,0))==0) {
+ snd_iprintf(buffer, "%hu\n",jog_sens);
+ } else {
+ printk(KERN_WARNING"%s() get_jogwheel_sensitivity() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_talkover_atten_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u16 talkover_atten;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &talkover_atten)) != 1) {
+ break;
+ }
+ if ((rc=set_talkover_att(ubulk, talkover_atten))!=0) {
+ printk(KERN_WARNING"%s() set_talkover_att failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_talkover_atten_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 talkover_atten;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_talkover_att(ubulk,&talkover_atten,1))==0) {
+ snd_iprintf(buffer, "%hx\n",talkover_atten);
+ } else {
+ printk(KERN_WARNING"%s() get_talkover_att() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_talkover_enable_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u8 talkover_enable;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hhu", &talkover_enable)) != 1) {
+ break;
+ }
+ if ((rc=set_talkover_enable(ubulk, talkover_enable))!=0) {
+ printk(KERN_WARNING"%s() set_talkover_enable failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_talkover_enable_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u8 talkover_enable;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_talkover_enable(ubulk,&talkover_enable))==0) {
+ snd_iprintf(buffer, "%hu\n",talkover_enable);
+ } else {
+ printk(KERN_WARNING"%s() get_talkover_enable() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_djconfig_word_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ char line[64];
+ u32 djconfig;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%x", &djconfig)) != 1) {
+ break;
+ }
+ if ((rc=set_djconsole_device_config(chip->index, djconfig, 0))!=0) {
+ printk(KERN_WARNING"%s() set_djconsole_device_config failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_djconfig_word_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 djconfig;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ if ((rc = get_djconsole_device_config(chip->index,&djconfig,1))==0) {
+ snd_iprintf(buffer, "%hx\n",djconfig);
+ } else {
+ printk(KERN_WARNING"%s() get_djconsole_device_config() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_audio_config_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u16 audio_config;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &audio_config)) != 1) {
+ break;
+ }
+ if ((rc=set_audio_config(ubulk, audio_config))!=0) {
+ printk(KERN_WARNING"%s() set_audio_config failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_audio_config_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 audio_config;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_audio_config(ubulk,&audio_config,1))==0) {
+ snd_iprintf(buffer, "%hx\n",audio_config);
+ } else {
+ printk(KERN_WARNING"%s() get_audio_config() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_mouse_enable_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ char line[64];
+ u8 mouse_enable;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hhu", &mouse_enable)) != 1) {
+ break;
+ }
+ if ((rc=set_mouse_state(chip, mouse_enable))!=0) {
+ printk(KERN_WARNING"%s() set_mouse_state failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_mouse_enable_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 mouse_state;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ if ((rc = get_mouse_state(chip,&mouse_state))==0) {
+ snd_iprintf(buffer, "%hu\n",mouse_state);
+ } else {
+ printk(KERN_WARNING"%s() get_mouse_state() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_sample_rate_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u16 sample_rate;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &sample_rate)) != 1) {
+ break;
+ }
+ if ((rc=set_sample_rate(ubulk, sample_rate))!=0) {
+ printk(KERN_WARNING"%s() set_sample_rate failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_sample_rate_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 sample_rate;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_sample_rate(ubulk,&sample_rate,1))==0) {
+ snd_iprintf(buffer, "%hu\n",sample_rate);
+ } else {
+ printk(KERN_WARNING"%s() get_sample_rate() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_xfader_lock_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u16 xfader_lock;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &xfader_lock)) != 1) {
+ break;
+ }
+ if ((rc=set_crossfader_lock(ubulk, xfader_lock))!=0) {
+ printk(KERN_WARNING"%s() set_crossfader_lock failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_xfader_lock_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 xfader_lock;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_crossfader_lock(ubulk,&xfader_lock,1))==0) {
+ snd_iprintf(buffer, "%hx\n",xfader_lock);
+ } else {
+ printk(KERN_WARNING"%s() get_crossfader_lock() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_xfader_curve_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u16 xfader_curve;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &xfader_curve)) != 1) {
+ break;
+ }
+ if ((rc=set_crossfader_style(ubulk, xfader_curve))!=0) {
+ printk(KERN_WARNING"%s() set_crossfader_style failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_xfader_curve_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 xfader_curve;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_crossfader_style(ubulk,&xfader_curve))==0) {
+ snd_iprintf(buffer, "%hx\n",xfader_curve);
+ } else {
+ printk(KERN_WARNING"%s() get_crossfader_style() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_shift_mode_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u8 shift_mode;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hhu", &shift_mode)) != 1) {
+ break;
+ }
+ if ((rc=set_mode_shift_state(ubulk, shift_mode))!=0) {
+ printk(KERN_WARNING"%s() set_mode_shift_state failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_shift_mode_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u8 shift_mode;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_mode_shift_state(ubulk,&shift_mode))==0) {
+ snd_iprintf(buffer, "%hhu\n",shift_mode);
+ } else {
+ printk(KERN_WARNING"%s() get_crossfader_style() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_fx_state_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ char line[64];
+ u16 fx_state;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &fx_state)) != 1) {
+ break;
+ }
+ if ((rc=set_fx_state(ubulk, fx_state))!=0) {
+ printk(KERN_WARNING"%s() set_fx_state failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_fx_state_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk* ubulk;
+ int chip_index = (int)(unsigned long)entry->private_data;
+ int rc;
+ u16 fx_state;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ ubulk = bulk_from_chip(chip);
+ if (ubulk!=NULL) {
+ if ((rc = get_fx_state(ubulk,&fx_state))==0) {
+ snd_iprintf(buffer, "%hx\n",fx_state);
+ } else {
+ printk(KERN_WARNING"%s() get_fx_state() failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_midi_channel_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct snd_hdjmidi* umidi;
+ char line[64];
+ u16 channel_to_set;
+ int rc, num;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ umidi = midi_from_chip(chip);
+ if (umidi!=NULL) {
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if ((num=sscanf(line, "%hx", &channel_to_set)) != 1) {
+ break;
+ }
+ if ((rc=set_midi_channel(chip, (u16*)&channel_to_set))!=0) {
+ printk(KERN_WARNING"%s() set_midi_channel failed, rc:%d\n",
+ __FUNCTION__,rc);
+ }
+ break;
+ }
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+
+static void proc_midi_channel_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ struct snd_hdjmidi* umidi;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ umidi = midi_from_chip(chip);
+ if (umidi!=NULL) {
+ snd_iprintf(buffer, "%x\n",atomic_read(&umidi->channel));
+ }
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+static void proc_chip_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ snd_iprintf(buffer, "%04x:%04x\n",
+ USB_ID_VENDOR(chip->usb_id),
+ USB_ID_PRODUCT(chip->usb_id));
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+/* Common proc files to show the usb device info. */
+static void proc_chip_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+ struct snd_hdj_chip *chip;
+ int chip_index = (int)(unsigned long)entry->private_data;
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip!=NULL) {
+ snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum);
+ dec_chip_ref_count(chip_index);
+ }
+}
+
+/* for scripts or debugging */
+static void snd_hdj_chip_create_proc(struct snd_hdj_chip *chip)
+{
+ struct snd_info_entry *entry;
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) )
+ if (! snd_card_proc_new(chip->card, "usbbus", &entry))
+ snd_info_set_text_ops(entry, (void*)(unsigned long)chip->index, proc_chip_usbbus_read);
+ if (! snd_card_proc_new(chip->card, "usbid", &entry))
+ snd_info_set_text_ops(entry, (void*)(unsigned long)chip->index, proc_chip_usbid_read);
+
+ if (! snd_card_proc_new(chip->card, "driver_name", &entry))
+ snd_info_set_text_ops(entry, (void*)(unsigned long)chip->index, proc_drivername_read);
+
+ if (! snd_card_proc_new(chip->card, "short_name", &entry))
+ snd_info_set_text_ops(entry, (void*)(unsigned long)chip->index, proc_shortname_read);
+
+ if (! snd_card_proc_new(chip->card, "long_name", &entry))
+ snd_info_set_text_ops(entry, (void*)(unsigned long)chip->index, proc_longname_read);
+
+ if ( chip->caps.midi==1 &&
+ snd_card_proc_new(chip->card, "midi_channel", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_midi_channel_read);
+ entry->c.text.write = proc_midi_channel_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (! snd_card_proc_new(chip->card, "fw_version", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_fw_version_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (! snd_card_proc_new(chip->card, "location_id", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_location_id_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (! snd_card_proc_new(chip->card, "driver_version", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_driver_version_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (! snd_card_proc_new(chip->card, "product_code", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_product_code_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (! snd_card_proc_new(chip->card, "netlink_unit", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_netlink_unit_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+
+ if (chip->caps.jog_locking==1 &&
+ snd_card_proc_new(chip->card, "jog_lock", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_jog_lock_read);
+ entry->c.text.write = proc_jog_lock_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.jog_sensitivity==1 &&
+ snd_card_proc_new(chip->card, "jog_sensitivity", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_jog_sens_read);
+ entry->c.text.write = proc_jog_sens_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.talkover_atten==1 &&
+ snd_card_proc_new(chip->card, "talkover_atten", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_talkover_atten_read);
+ entry->c.text.write = proc_talkover_atten_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.talkover_atten==1 &&
+ snd_card_proc_new(chip->card, "talkover_enable", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_talkover_enable_read);
+ entry->c.text.write = proc_talkover_enable_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.djconfig_word==1 &&
+ snd_card_proc_new(chip->card, "djconfig_word", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_djconfig_word_read);
+ entry->c.text.write = proc_djconfig_word_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.audio_config==1 &&
+ snd_card_proc_new(chip->card, "audio_config", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_audio_config_read);
+ entry->c.text.write = proc_audio_config_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.mouse==1 &&
+ snd_card_proc_new(chip->card, "mouse_enable", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_mouse_enable_read);
+ entry->c.text.write = proc_mouse_enable_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.sample_rate_readable==1 &&
+ snd_card_proc_new(chip->card, "sample_rate", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_sample_rate_read);
+ if (chip->caps.sample_rate_writable==1) {
+ entry->c.text.write = proc_sample_rate_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ } else {
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ }
+ if (chip->caps.xfader_lock==1 &&
+ snd_card_proc_new(chip->card, "xfader_lock", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_xfader_lock_read);
+ entry->c.text.write = proc_xfader_lock_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.xfader_curve==1 &&
+ snd_card_proc_new(chip->card, "xfader_curve", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_xfader_curve_read);
+ entry->c.text.write = proc_xfader_curve_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.shift_mode==1 &&
+ snd_card_proc_new(chip->card, "shift_mode", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_shift_mode_read);
+ entry->c.text.write = proc_shift_mode_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.shift_mode==1 &&
+ snd_card_proc_new(chip->card, "fx_state", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ proc_fx_state_read);
+ entry->c.text.write = proc_fx_state_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+#else
+ if (! snd_card_proc_new(chip->card, "usbbus", &entry))
+ snd_info_set_text_ops(entry, (void*)(unsigned long)chip->index, 1024, proc_chip_usbbus_read);
+ if (! snd_card_proc_new(chip->card, "usbid", &entry))
+ snd_info_set_text_ops(entry, (void*)(unsigned long)chip->index, 1024, proc_chip_usbid_read);
+
+ if ( chip->caps.midi==1 &&
+ snd_card_proc_new(chip->card, "midi_channel", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_midi_channel_read);
+ entry->c.text.write = proc_midi_channel_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (! snd_card_proc_new(chip->card, "fw_version", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_fw_version_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (! snd_card_proc_new(chip->card, "location_id", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_location_id_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (! snd_card_proc_new(chip->card, "driver_version", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_driver_version_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (! snd_card_proc_new(chip->card, "product_code", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_product_code_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (! snd_card_proc_new(chip->card, "netlink_unit", &entry)) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_netlink_unit_read);
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+
+ if (chip->caps.jog_locking==1 &&
+ snd_card_proc_new(chip->card, "jog_lock", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_jog_lock_read);
+ entry->c.text.write = proc_jog_lock_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.jog_sensitivity==1 &&
+ snd_card_proc_new(chip->card, "jog_sensitivity", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_jog_sens_read);
+ entry->c.text.write = proc_jog_sens_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.talkover_atten==1 &&
+ snd_card_proc_new(chip->card, "talkover_atten", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_talkover_atten_read);
+ entry->c.text.write = proc_talkover_atten_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.talkover_atten==1 &&
+ snd_card_proc_new(chip->card, "talkover_enable", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_talkover_enable_read);
+ entry->c.text.write = proc_talkover_enable_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.djconfig_word==1 &&
+ snd_card_proc_new(chip->card, "djconfig_word", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_djconfig_word_read);
+ entry->c.text.write = proc_djconfig_word_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.audio_config==1 &&
+ snd_card_proc_new(chip->card, "audio_config", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_audio_config_read);
+ entry->c.text.write = proc_audio_config_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.mouse==1 &&
+ snd_card_proc_new(chip->card, "mouse_enable", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_mouse_enable_read);
+ entry->c.text.write = proc_mouse_enable_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.sample_rate_readable==1 &&
+ snd_card_proc_new(chip->card, "sample_rate", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_sample_rate_read);
+ if (chip->caps.sample_rate_writable==1) {
+ entry->c.text.write = proc_sample_rate_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ } else {
+ entry->mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ }
+ if (chip->caps.xfader_lock==1 &&
+ snd_card_proc_new(chip->card, "xfader_lock", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_xfader_lock_read);
+ entry->c.text.write = proc_xfader_lock_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.xfader_curve==1 &&
+ snd_card_proc_new(chip->card, "xfader_curve", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_xfader_curve_read);
+ entry->c.text.write = proc_xfader_curve_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.shift_mode==1 &&
+ snd_card_proc_new(chip->card, "shift_mode", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_shift_mode_read);
+ entry->c.text.write = proc_shift_mode_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+ if (chip->caps.shift_mode==1 &&
+ snd_card_proc_new(chip->card, "fx_state", &entry)==0) {
+ snd_info_set_text_ops(entry,
+ (void*)(unsigned long)chip->index,
+ 1024,
+ proc_fx_state_read);
+ entry->c.text.write = proc_fx_state_write;
+ entry->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+#endif
+}
+
+/*
+ * Currently we only manage 1 interface, so this is much simplified from USBAUDIO. If this changes in the
+ * future, then this code must be updated to reflect this.
+ */
+static int snd_hdj_create_streams(struct snd_hdj_chip *chip, int ifnum)
+{
+ struct usb_interface* intf = NULL, *original_intf = NULL;
+
+ if (midi_intf_num_full_controller_num_check(chip,ifnum)==1) {
+ /* this will be referenced with a call to usb_get_intf */
+ intf = usb_ifnum_to_if(chip->dev,ifnum);
+ if(intf==NULL) {
+ printk(KERN_WARNING"%s(): usb_ifnum_to_if(): returned NULL, bailing\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ /* We are ready to create the MIDI interface */
+ return snd_hdj_create_midi_interface(chip, intf, NULL);
+ } else if (bulk_intf_num_check(chip,ifnum)==1) {
+ /* this will be referenced with a call to usb_get_intf */
+ intf = usb_ifnum_to_if(chip->dev,ifnum);
+ if(intf==NULL) {
+ snd_printk(KERN_WARNING"%s(): usb_ifnum_to_if(): returned NULL, bailing\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ /* We are ready to create the Bulk interface */
+ return hdj_create_bulk_interface(chip, intf);
+ } else if (midimp3_intf_num_full_controller_num_check(chip,ifnum)==1) {
+ /* reference the original intf */
+ original_intf = usb_ifnum_to_if(chip->dev,ifnum);
+ if (original_intf==NULL) {
+ snd_printk(KERN_WARNING"%s(): usb_ifnum_to_if(): returned NULL (original mp3 intf), bailing\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+
+ /* we need to refer to interface 0 for the control data */
+ ifnum = DJ_MP3_HID_IF_NUM;
+ /* this will be referenced with a call to usb_get_intf */
+ intf = usb_ifnum_to_if(chip->dev,ifnum);
+ if (intf==NULL) {
+ snd_printk(KERN_WARNING"snd_hdj_create_streams(): usb_ifnum_to_if(): returned NULL, bailing\n");
+ return -EINVAL;
+ }
+ /* We are ready to create the MIDI interface */
+ return snd_hdj_create_midi_interface(chip, intf, original_intf);
+ }
+ else if ((ifnum==DJ_ASIO_DJ1_IF_NUM && chip->product_code==DJCONSOLE_PRODUCT_CODE) ||
+ (ifnum==DJ_ASIO_MK2_RMX_IF_NUM && chip->product_code==DJCONSOLE2_PRODUCT_CODE) ||
+ (ifnum==DJ_ASIO_MK2_RMX_IF_NUM && chip->product_code==DJCONSOLERMX_PRODUCT_CODE)) {
+ /* The ASIO interface isn't used- but return success so that the user does not
+ * see an error in the log */
+ return 0;
+ }
+ else{
+ snd_printk(KERN_WARNING"snd_hdj_create_streams(): nothing found for interface: %d and product: %d, bailing\n", ifnum, chip->product_code);
+ }
+
+ return -EINVAL;
+}
+
+/* MARK: PRODCHANGE */
+/* sets up the capabilities of our DJ device */
+static void snd_hdj_enter_caps(struct snd_hdj_chip *chip)
+{
+ memset(&chip->caps,0,sizeof(chip->caps));
+ if (chip->product_code==DJCONSOLE_PRODUCT_CODE) {
+ chip->caps.port_mode = 1;
+ chip->caps.num_out_ports = 1;
+ chip->caps.num_in_ports = 1;
+ chip->caps.talkover_atten = 1;
+ chip->caps.djconfig_word = 1;
+ chip->caps.mouse = 1;
+ chip->caps.sample_rate_readable = 1;
+ chip->caps.sample_rate_writable = 1;
+ chip->caps.midi = 1;
+ chip->caps.leds_hid_controlled = 1;
+ chip->caps.leds_report_len = DJC_SET_REPORT_LEN;
+ chip->caps.leds_report_id = DJC_SET_REPORT_ID;
+
+ chip->caps.audio_board_present = 1;
+ chip->caps.audio_board_upgradeable = 1;
+ chip->caps.audio_board_upgradeable_full = 1;
+ chip->caps.audio_board_upgradeable_partial = 0;
+ chip->caps.audio_board_version = 0; /* to be filled by bulk later */
+
+ chip->caps.controller_board_present = 1;
+ chip->caps.controller_board_upgradeable = 0;
+ chip->caps.controller_board_version = 0; /* to be filled by bulk later */
+ chip->caps.controller_type = CONTROLLER_TYPE_PSOC_26;
+ } else if (chip->product_code==DJCONSOLE2_PRODUCT_CODE) {
+ chip->caps.num_out_ports = 1;
+ chip->caps.num_in_ports = 1;
+ chip->caps.talkover_atten = 1;
+ chip->caps.audio_config = 1;
+ chip->caps.mouse = 1;
+ chip->caps.xfader_lock = 1;
+ chip->caps.xfader_curve = 1;
+ chip->caps.midi = 1;
+ chip->caps.sample_rate_readable = 1;
+ chip->caps.leds_hid_controlled = 1;
+ chip->caps.leds_report_len = DJMK2_SET_REPORT_LEN;
+ chip->caps.leds_report_id = DJMK2_SET_REPORT_ID;
+
+ chip->caps.audio_board_present = 1;
+ chip->caps.audio_board_upgradeable = 1;
+ chip->caps.audio_board_upgradeable_full = 1;
+ chip->caps.audio_board_upgradeable_partial = 1;
+ chip->caps.audio_board_version = 0; /* to be filled by bulk later */
+ chip->caps.audio_board_in_boot_mode = 0; /* to be filled by bulk later */
+
+ chip->caps.controller_board_present = 1;
+ chip->caps.controller_board_upgradeable = 0; /* to be filled by bulk later */
+ chip->caps.controller_board_version = 0; /* to be filled by bulk later */
+ chip->caps.controller_type = CONTROLLER_TYPE_UNKNOWN; /* to be filled by bulk later */
+ chip->caps.controller_board_in_boot_mode = 0; /* to be filled by bulk later */
+
+ } else if (chip->product_code==DJCONSOLERMX_PRODUCT_CODE) {
+ chip->caps.non_volatile_channel = 1;
+ chip->caps.num_out_ports = 1;
+ chip->caps.num_in_ports = 1;
+ chip->caps.jog_locking = 1;
+ chip->caps.jog_sensitivity = 1;
+ chip->caps.talkover_atten = 1;
+ chip->caps.audio_config = 1;
+ chip->caps.midi = 1;
+ chip->caps.sample_rate_readable = 1;
+ chip->caps.leds_hid_controlled = 1;
+ chip->caps.leds_report_len = DJRMX_SET_REPORT_LEN;
+ chip->caps.leds_report_id = DJRMX_SET_REPORT_ID;
+
+ chip->caps.audio_board_present = 1;
+ chip->caps.audio_board_upgradeable = 1;
+ chip->caps.audio_board_upgradeable_full = 1;
+ chip->caps.audio_board_upgradeable_partial = 1;
+ chip->caps.audio_board_version = 0; /* to be filled by bulk later */
+ chip->caps.audio_board_in_boot_mode = 0; /* to be filled by bulk later */
+
+ chip->caps.controller_board_present = 1;
+ chip->caps.controller_board_upgradeable = 0; /* to be filled by bulk later */
+ chip->caps.controller_board_version = 0; /* to be filled by bulk later */
+ chip->caps.controller_type = CONTROLLER_TYPE_UNKNOWN; /* to be filled by bulk later */
+ chip->caps.controller_board_in_boot_mode = 0; /* to be filled by bulk later */
+ } else if (chip->product_code==DJCONTROLSTEEL_PRODUCT_CODE) {
+ chip->caps.non_volatile_channel = 1;
+ chip->caps.num_out_ports = 1;
+ chip->caps.num_in_ports = 1;
+ chip->caps.jog_locking = 1;
+ chip->caps.jog_sensitivity = 1;
+ chip->caps.shift_mode = 1;
+ chip->caps.midi = 1;
+ chip->caps.leds_bulk_controlled = 1;
+ chip->caps.leds_report_len = DJ_CONTROL_STEEL_BULK_TRANSFER_SIZE;
+ chip->caps.leds_report_id = DJ_STEEL_STANDARD_SET_LED_REPORT;
+
+ chip->caps.controller_board_present = 1;
+ chip->caps.controller_board_upgradeable = 1;
+ chip->caps.controller_board_version = 0; /* to be filled by bulk later */
+ chip->caps.controller_type = CONTROLLER_TYPE_UNKNOWN; /* to be filled by bulk later */
+ } else if (chip->product_code==DJCONTROLLER_PRODUCT_CODE) {
+ chip->caps.hid_support_only = 1;
+ chip->caps.hid_interface_to_poll = DJ_MP3_HID_IF_NUM;
+ chip->caps.num_out_ports = 1;
+ chip->caps.num_in_ports = 1;
+ chip->caps.midi = 1;
+ chip->caps.leds_hid_controlled = 1;
+ chip->caps.leds_report_len = DJ_MP3_HID_OUTPUT_REPORT_LEN;
+ chip->caps.leds_report_id = DJ_MP3_HID_REPORT_ID;
+
+ chip->caps.controller_board_present = 1;
+ chip->caps.controller_board_upgradeable = 0;
+ chip->caps.controller_board_version = le16_to_cpu(chip->dev->descriptor.bcdDevice);
+ if (chip->usb_id==USB_ID(USB_HDJ_VENDOR_ID, DJ_CONTROL_MP3W_PID)) {
+ chip->caps.controller_type = CONTROLLER_TYPE_WELTREND;
+ } else {
+ chip->caps.controller_type = CONTROLLER_TYPE_PSOC_26;
+ }
+ } else {
+ printk(KERN_WARNING"%s() invalid product:%d\n",__FUNCTION__,chip->product_code);
+ }
+}
+
+/* Free the chip instance. */
+static int snd_hdj_chip_free(struct snd_hdj_chip *chip)
+{
+ struct list_head *p;
+ struct list_head *next;
+ struct snd_hdjmidi* umidi;
+ struct usb_hdjbulk* ubulk;
+
+ /* frees resources of all MIDI interfaces attached to this chip */
+ if (!list_empty(&chip->midi_list)) {
+ list_for_each_safe(p,next,&chip->midi_list) {
+ umidi = list_entry(p, struct snd_hdjmidi, list);
+ list_del(p);
+ snd_hdj_free(umidi);
+ }
+ }
+
+ /* frees resources of all bulk interfaces attached to this chip */
+ if (!list_empty(&chip->bulk_list)) {
+ list_for_each_safe(p,next,&chip->bulk_list) {
+ ubulk = list_entry(p, struct usb_hdjbulk, list);
+ list_del(p);
+ /* decrement our bulk usage count */
+ kref_put(&ubulk->kref, hdj_delete);
+ }
+ }
+
+ hdj_kill_chip_urbs(chip);
+
+ if(chip->ctrl_req_buffer != NULL)
+ {
+ usb_buffer_free(chip->dev,
+ chip->ctrl_urb->transfer_buffer_length,
+ chip->ctrl_req_buffer,
+ chip->ctrl_urb->transfer_dma);
+ chip->ctrl_req_buffer = NULL;
+ }
+
+ if(chip->ctrl_urb != NULL)
+ {
+ usb_free_urb(chip->ctrl_urb);
+ chip->ctrl_urb = NULL;
+ }
+
+
+ if(chip->ctl_req != NULL)
+ {
+ usb_buffer_free(chip->dev,
+ sizeof(*(chip->ctl_req)),
+ chip->ctl_req,
+ chip->ctl_req_dma);
+ chip->ctl_req = NULL;
+ }
+
+ /* Since we called usb_get_dev when creating the chip, we must
+ * call usb_put_dev here. This was done because the device might be accessed briefly if
+ * MIDI being open prevents the chip destructor from being called right away.
+ * We associate the chip destructor with snd_card_free_when_closed
+ */
+ usb_put_dev(chip->dev);
+ kfree(chip);
+
+ return 0;
+}
+
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) )
+static int snd_hdj_chip_dev_free(struct snd_device *device)
+#else
+static int snd_hdj_chip_dev_free(snd_device_t *device)
+#endif
+{
+ struct snd_hdj_chip *chip = device->device_data;
+ return snd_hdj_chip_free(chip);
+}
+
+/* MARK: PRODCHANGE */
+static void create_id(char *card_id, u32 id_buf_len, int product_code, int card_num)
+{
+ if (product_code==DJCONSOLE_PRODUCT_CODE) {
+ snprintf(card_id,id_buf_len-1,"herc_djc%d",card_num);
+ } else if (product_code==DJCONSOLE2_PRODUCT_CODE) {
+ snprintf(card_id,id_buf_len-1,"herc_djmk2%d",card_num);
+ } else if (product_code==DJCONSOLERMX_PRODUCT_CODE) {
+ snprintf(card_id,id_buf_len-1,"herc_djrmx%d",card_num);
+ } else if (product_code==DJCONTROLSTEEL_PRODUCT_CODE) {
+ snprintf(card_id,id_buf_len-1,"herc_djst%d",card_num);
+ } else if (product_code==DJCONTROLLER_PRODUCT_CODE) {
+ snprintf(card_id,id_buf_len-1,"herc_djmp3%d",card_num);
+ } else {
+ printk(KERN_WARNING"%s() invalid product:%d\n",__FUNCTION__,product_code);
+ }
+}
+
+/* Create a chip instance and set its names. *
+ * NOTE: we must clean up here if something goes wrong, because the chip
+ * cleanup routine will not fire */
+static int snd_hdj_chip_create(struct usb_device *dev,
+ int idx,
+ int product_code,
+ struct snd_hdj_chip **rchip)
+{
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) )
+ struct snd_card *card = NULL;
+#else
+ snd_card_t *card = NULL;
+#endif
+ struct snd_hdj_chip *chip = NULL;
+ int err=-ENOMEM, len;
+ char component[36];
+ char card_id[36];
+ char shortname[50];
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) )
+ static struct snd_device_ops ops = {
+ .dev_free = snd_hdj_chip_dev_free,
+ };
+#else
+ static snd_device_ops_t ops = {
+ .dev_free = snd_hdj_chip_dev_free,
+ };
+#endif
+
+ *rchip = NULL;
+
+ /* if an id was supplied from the command line, put it in, otherwise hard coded one */
+ memset(card_id,0,sizeof(card_id));
+ if (id[idx]==NULL) {
+ /* create a custom id */
+ create_id(card_id, sizeof(card_id), product_code, idx);
+ } else {
+ /* let the kernel option override custom id */
+ strncpy(card_id,id[idx],sizeof(card_id)-1);
+ }
+ card = snd_card_new(index[idx], card_id/*id[idx]*/, THIS_MODULE, 0);
+ if (card == NULL) {
+ snd_printk(KERN_WARNING "snd_hdj_chip_create(): cannot create card instance %d\n", idx);
+ return -ENOMEM;
+ }
+
+ /* save the index, so people who have the card can reference the chip */
+ card->private_data = (void*)(unsigned long)idx;
+
+ chip = kmalloc(sizeof(*chip), GFP_KERNEL);
+ if (! chip) {
+ snd_printk(KERN_WARNING "snd_hdj_chip_create(): cannot allocate memory for context %d\n", idx);
+ return -ENOMEM;
+ }
+
+ memset(chip,0,sizeof(*chip));
+ chip->index = idx;
+ chip->ref_count = 0;
+ chip->dev = dev;
+ chip->card = card;
+ chip->product_code = product_code;
+
+ init_MUTEX(&chip->vendor_request_mutex);
+
+ /* initialise the atomic variables */
+ atomic_set(&chip->locked_io, 0);
+ atomic_set(&chip->vendor_command_in_progress, 0);
+ atomic_set(&chip->shutdown, 0);
+ atomic_set(&chip->no_urb_submission, 0);
+ atomic_set(&chip->num_suspended_intf, 0);
+ atomic_set(&chip->next_midi_device, 0);
+ atomic_set(&chip->next_bulk_device, 0);
+
+ INIT_LIST_HEAD(&chip->midi_list);
+ INIT_LIST_HEAD(&chip->bulk_list);
+ chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+ le16_to_cpu(dev->descriptor.idProduct));
+ init_MUTEX(&chip->netlink_list_mutex);
+ INIT_LIST_HEAD(&chip->netlink_registered_processes);
+
+ /* fill in DJ capabilities for this device */
+ snd_hdj_enter_caps(chip);
+
+ /* Set this right away because caller might have to cleanup. Caller cleans up
+ * because the registration mutex is held now */
+ *rchip = chip;
+
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ printk(KERN_WARNING"%s() snd_device_new() failed, rc:%d\n",__FUNCTION__,err);
+ return err;
+ }
+
+ strcpy(card->driver, "hdj_mod");
+ sprintf(component, "USB%04x:%04x",
+ USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+ snd_component_add(card, component);
+
+ /* init chip completion */
+ init_completion(&chip->ctrl_completion);
+
+ /* allocate the urb */
+ chip->ctrl_urb = usb_alloc_urb(0,GFP_KERNEL);
+ if(chip->ctrl_urb==NULL) {
+ printk(KERN_ERR"snd_hdj_chip_create(): usb_alloc_urb() returned NULL, bailing\n");
+ return err;
+ }
+
+ /* allocate memory for setup packet for our control requests */
+ chip->ctl_req = usb_buffer_alloc(chip->dev,
+ sizeof(*(chip->ctl_req)),
+ GFP_KERNEL,
+ &chip->ctl_req_dma);
+ if(chip->ctl_req == NULL) {
+ printk(KERN_WARNING"snd_hdj_chip_create(): usb_buffer_alloc() failed for setup DMA\n");
+ return err;
+ }
+
+ /* get the control pipes */
+ chip->ctrl_out_pipe = usb_sndctrlpipe(chip->dev, 0);
+ chip->ctrl_in_pipe = usb_rcvctrlpipe(chip->dev, 0);
+
+ chip->ctrl_req_buffer_len = sizeof(u16);
+ chip->ctrl_urb->transfer_buffer_length = chip->ctrl_req_buffer_len;
+ chip->ctrl_req_buffer = usb_buffer_alloc(chip->dev,
+ chip->ctrl_urb->transfer_buffer_length,
+ GFP_KERNEL,
+ &chip->ctrl_urb->transfer_dma);
+ if (chip->ctrl_req_buffer == NULL) {
+ printk(KERN_WARNING"snd_hdj_chip_create(): usb_buffer_alloc() failed\n");
+ return err;
+ }
+
+ /* Retrieve the device string as shortname. */
+ if (!dev->descriptor.iProduct ||
+ usb_string(dev, dev->descriptor.iProduct,
+ card->shortname, sizeof(card->shortname)) <= 0) {
+ memset(card->shortname,0,sizeof(card->shortname));
+ /* no name available from anywhere, so use ID. */
+ snprintf(card->shortname, sizeof(card->shortname)-1,
+ "Hercules MIDI Dev %#04x:%#04x",
+ USB_ID_VENDOR(chip->usb_id),
+ USB_ID_PRODUCT(chip->usb_id));
+ }
+
+ /* prepend company name if not present */
+ if (strstr(card->shortname,"Hercules")==NULL) {
+ memset(shortname,0,sizeof(shortname));
+ snprintf(shortname,sizeof(shortname)-1,"Hercules ");
+ strncpy(shortname+strlen(shortname),
+ card->shortname,sizeof(shortname)+strlen(shortname)-1);
+ memset(card->shortname,0,sizeof(card->shortname));
+ strncpy(card->shortname,shortname,sizeof(card->shortname)-1);
+ }
+
+ /* Retrieve the vendor and device strings as longname. */
+ if (dev->descriptor.iManufacturer) {
+ len = usb_string(dev, dev->descriptor.iManufacturer,
+ card->longname, sizeof(card->longname));
+ } else {
+ len = 0;
+ /* We don't really care if there isn't any vendor string. */
+ }
+
+ if (len > 0) {
+ strlcat(card->longname, " ", sizeof(card->longname));
+ }
+
+ strlcat(card->longname, card->shortname, sizeof(card->longname));
+
+ len = strlcat(card->longname, " at ", sizeof(card->longname));
+
+ if (len < sizeof(card->longname)) {
+ usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
+ }
+
+ strlcat(card->longname,
+ snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
+ snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
+ ", high speed",
+ sizeof(card->longname));
+
+ usb_make_path(dev, &chip->usb_device_path[0],sizeof(chip->usb_device_path));
+ snd_hdj_chip_create_proc(chip);
+
+ /* initialize netlink in order to notifications to usermode */
+ if (init_netlink_state(chip)!=0) {
+ printk(KERN_WARNING"%s() init_netlink_state() failed\n",__FUNCTION__);
+ /* We do not make this fatal, because it is possible that there are
+ * no more netlink units available to us */
+ }
+
+ return 0;
+}
+
+/* MARK: PRODCHANGE */
+static int usbid_to_product_code(u32 usbid)
+{
+ if (usbid == USB_ID(USB_HDJ_VENDOR_ID, DJ_CONSOLE_PID)) {
+ return DJCONSOLE_PRODUCT_CODE;
+ } else if (usbid == USB_ID(USB_HDJ_VENDOR_ID, DJ_CONSOLE_MK2_PID)) {
+ return DJCONSOLE2_PRODUCT_CODE;
+ } else if (usbid == USB_ID(USB_HDJ_VENDOR_ID, DJ_CONSOLE_RMX_PID)) {
+ return DJCONSOLERMX_PRODUCT_CODE;
+ } else if (usbid == USB_ID(USB_HDJ_VENDOR_ID, DJ_CONSOLE_STEEL_PID)) {
+ return DJCONTROLSTEEL_PRODUCT_CODE;
+ } else if (usbid == USB_ID(USB_HDJ_VENDOR_ID, DJ_CONTROL_MP3_PID)) {
+ return DJCONTROLLER_PRODUCT_CODE;
+ } else if (usbid == USB_ID(USB_HDJ_VENDOR_ID, DJ_CONTROL_MP3W_PID)) {
+ return DJCONTROLLER_PRODUCT_CODE;
+ } else {
+ return DJCONSOLE_PRODUCT_UNKNOWN;
+ }
+}
+
+static int hdj_probe(struct usb_interface *interface, const struct usb_device_id *uid)
+{
+ int failed_retval = -ENODEV;
+ int i, product_code = DJCONSOLE_PRODUCT_UNKNOWN;
+ struct snd_hdj_chip *chip = NULL;
+ struct usb_host_interface *alts = NULL;
+ int ifnum = -1;
+ u32 usbid = 0;
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) )
+ struct snd_card *card = NULL;
+#else
+ snd_card_t *card = NULL;
+#endif
+ struct usb_device *usb_dev = NULL;
+
+ usb_dev = interface_to_usbdev(interface);
+ alts = &interface->altsetting[0];
+ ifnum = get_iface_desc(alts)->bInterfaceNumber;
+ usbid = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor),
+ le16_to_cpu(usb_dev->descriptor.idProduct));
+
+ if((le16_to_cpu(usb_dev->descriptor.idVendor)!=USB_HDJ_VENDOR_ID)&&
+ (le16_to_cpu(usb_dev->descriptor.idProduct)!=DJ_CONSOLE_PID)&&
+ (le16_to_cpu(usb_dev->descriptor.idProduct)!=DJ_CONSOLE_MK2_PID)&&
+ (le16_to_cpu(usb_dev->descriptor.idProduct)!=DJ_CONSOLE_RMX_PID)&&
+ (le16_to_cpu(usb_dev->descriptor.idProduct)!=DJ_CONTROL_MP3_PID)&&
+ (le16_to_cpu(usb_dev->descriptor.idProduct)!=DJ_CONTROL_MP3W_PID)&&
+ (le16_to_cpu(usb_dev->descriptor.idProduct)!=DJ_CONSOLE_STEEL_PID))
+ {
+ printk(KERN_INFO"hdj_probe() unsupported device, idVendor%lx, idProduct:%lx\n",
+ (unsigned long)le16_to_cpu(usb_dev->descriptor.idVendor),
+ (unsigned long)le16_to_cpu(usb_dev->descriptor.idProduct));
+ return failed_retval;
+ }
+
+ /* Extract the product code, and bail if the product is unsupported */
+ product_code = usbid_to_product_code(usbid);
+ if (product_code == DJCONSOLE_PRODUCT_UNKNOWN) {
+ snd_printk(KERN_ERR "hdj_probe(): unsupported usbid:%x\n",usbid);
+ return failed_retval;
+ }
+
+ /* TODO- settle the wait time with Mark */
+ /* TODO- settle which devices require wait- ask Mark */
+ /* We need to wait until the TUSB loads the topboard information*/
+ msleep(500);
+
+ /*
+ * Check whether it's already registered. I kept this here from usbaudio just in case in the future
+ * we have multiple interfaces to manage on the same device. Unlikely, but then this does not add too
+ * much complication, nor does it make for unreadable code, I think. Currently, all devices which
+ * this module drives only have 1 MIDI/bulk interface to manage. If this changes, then
+ * snd_hdj_create_streams will have to look for multiple interfaces.
+ */
+ chip = NULL;
+ down(®ister_mutex);
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (usb_chip[i]!=NULL && usb_chip[i]->dev == usb_dev) {
+ if (atomic_read(&usb_chip[i]->shutdown)) {
+ snd_printk(KERN_WARNING"hdj_probe(): USB device is in the shutdown state, cannot create a card instance\n");
+ up(®ister_mutex);
+ goto __error_no_dec;
+ }
+ chip = usb_chip[i];
+ break;
+ }
+ }
+
+ if(chip==NULL) {
+ /*
+ it's a fresh one.
+ Now look for an empty slot and create a new card instance.
+ */
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (enable[i] && ! usb_chip[i] &&
+ (vid[i] == -1 || vid[i] == USB_ID_VENDOR(usbid)) &&
+ (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(usbid))) {
+ /* we do this here instead of in snd_hdj_chip_create because of
+ * cleanup issues */
+ usb_dev = usb_get_dev(usb_dev);
+ if (usb_dev==NULL) {
+ printk(KERN_WARNING"%s() usb_get_dev() failed, bailing\n",
+ __FUNCTION__);
+ up(®ister_mutex);
+ return failed_retval;
+ }
+ if (snd_hdj_chip_create(usb_dev, i, product_code, &chip) < 0) {
+ snd_printk(KERN_WARNING"hdj_probe(): snd_hdj_chip_create failed\n");
+ if (chip!=NULL && chip->card!=NULL) {
+ /* for cleanup */
+ snd_card_set_dev(chip->card, &interface->dev);
+ }
+ up(®ister_mutex);
+ goto __error_no_dec;
+ }
+ snd_card_set_dev(chip->card, &interface->dev);
+ break;
+ }
+ }
+
+ if (! chip) {
+ snd_printk(KERN_WARNING "hdj_probe(): no available usb midi device entry\n");
+ up(®ister_mutex);
+ goto __error_no_dec;
+ } else {
+ /* for the goto __error path */
+ card = chip->card;
+ }
+ }
+ usb_chip[chip->index] = chip;
+ atomic_inc(&chip->num_interfaces);
+ usb_set_intfdata(interface, (void *)(unsigned long)chip->index);
+ card = chip->card;
+ up(®ister_mutex);
+
+ /* each supported interface must increment the chip reference count */
+ inc_chip_ref_count(chip->index);
+
+ if (snd_hdj_create_streams(chip, ifnum) < 0) {
+ snd_printk(KERN_WARNING"hdj_probe(): snd_usb_create_streams() failed\n");
+ goto __error;
+ }
+
+ /* we are allowed to call snd_card_register() many times */
+ if (snd_card_register(chip->card) < 0) {
+ snd_printk(KERN_WARNING"hdj_probe(): snd_card_register() failed\n");
+ goto __error;
+ }
+
+ return 0;
+__error:
+ snd_printk(KERN_WARNING"hdj_probe(): reached __error\n");
+ chip = dec_chip_ref_count(chip->index);
+ return failed_retval;
+__error_no_dec:
+ snd_printk(KERN_WARNING"hdj_probe(): reached __error_no_dec\n");
+ /* cleanup, if we have to do so */
+ if (chip!=NULL) {
+ if (chip->card!=NULL) {
+ snd_card_disconnect(card);
+ snd_card_free(card);
+ chip->card = NULL;
+ }
+ snd_hdj_chip_free(chip);
+ }
+ return failed_retval;
+}
+
+struct snd_hdj_chip* inc_chip_ref_count(int chip_index)
+{
+ struct snd_hdj_chip* chip = NULL;
+ if(chip_index >= SNDRV_CARDS || chip_index < 0) {
+ /*invalid index*/
+ return NULL;
+ }
+
+ down(®ister_mutex);
+ if(usb_chip[chip_index] != NULL && atomic_read(&usb_chip[chip_index]->shutdown)!=1) {
+ chip = usb_chip[chip_index];
+ ++(chip->ref_count);
+ }
+
+ up(®ister_mutex);
+
+ return chip;
+}
+
+struct snd_hdj_chip* dec_chip_ref_count(int chip_index)
+{
+ struct snd_hdj_chip* chip = NULL;
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) )
+ struct snd_card *card;
+#else
+ snd_card_t *card;
+#endif
+ struct list_head *p;
+ struct list_head *next;
+
+ if(chip_index >= SNDRV_CARDS || chip_index < 0) {
+ /*invalid index*/
+ return NULL;
+ }
+ down(®ister_mutex);
+ if(usb_chip[chip_index] != NULL) {
+ chip = usb_chip[chip_index];
+ --(chip->ref_count);
+
+ if(chip->ref_count <= 0) {
+ card = chip->card;
+ /* remove the chip from the list here- no one else can now access it
+ * except this routine and the chip destructor, associated with the card's
+ * "low level device" */
+ usb_chip[chip_index] = NULL;
+ atomic_set(&chip->shutdown, 1);
+
+ /* we have no callback associated with disconnect */
+ snd_card_disconnect(card);
+
+ /* release the midi resources */
+ if (!list_empty(&chip->midi_list)) {
+ list_for_each_safe(p,next,&chip->midi_list) {
+ snd_hdjmidi_disconnect(p);
+ }
+ }
+
+ /* release the bulk resources */
+ if (!list_empty(&chip->bulk_list)) {
+ list_for_each_safe(p,next,&chip->bulk_list) {
+ hdjbulk_disconnect(p);
+ }
+ }
+
+ /* Free all remaining registered processes- no one is allowed to register
+ * anymore, because the chip is being destroyed and cannot be reacquired */
+ uninit_netlink_state(chip);
+
+ /* Freeing the card results in the chip cleanup routine (called the
+ * chip's destructor) being executed, but only when no client has
+ * the MIDI device open through ALSA */
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) )
+ snd_card_free_when_closed(card);
+#else
+ snd_card_free(card);
+#endif
+ up(®ister_mutex);
+
+ /* the chip has been destroyed */
+ return NULL;
+ }
+ }
+
+ up(®ister_mutex);
+ return chip;
+}
+
+/* since we could be "alive" for a bit of time after disconnect if a usermode
+ * client maintains a handle, we reference interface and devices */
+void reference_usb_intf_and_devices(struct usb_interface *intf)
+{
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) )
+ usb_get_intf(intf);
+ get_device(&intf->dev);
+ usb_get_dev(interface_to_usbdev(intf));
+#else
+ /* this is the only recourse */
+ get_device(&ubulk->iface->dev);
+ usb_get_dev(interface_to_usbdev(ubulk->iface));
+#endif
+}
+
+void dereference_usb_intf_and_devices(struct usb_interface *intf)
+{
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) )
+ put_device(&intf->dev);
+ usb_put_dev(interface_to_usbdev(intf));
+ usb_put_intf(intf);
+#else
+ usb_put_dev(interface_to_usbdev(intf));
+ /* this is the only recourse */
+ put_device(&intf->dev);
+#endif
+}
+
+/* warning: assumes that inc_chip_ref_count() has been called on the chip */
+struct usb_hdjbulk * bulk_from_chip(struct snd_hdj_chip* chip)
+{
+ struct usb_hdjbulk *ubulk = NULL;
+ /* assume only one MIDI device per chip- otherwise expand function parameters */
+ if (list_empty(&chip->bulk_list)) {
+ printk(KERN_WARNING"%s() bulk_list empty, bailing\n",__FUNCTION__);
+ return NULL;
+ }
+ ubulk = list_entry(chip->bulk_list.next, struct usb_hdjbulk, list);
+ return ubulk;
+}
+
+/* warning: assumes that inc_chip_ref_count() has been called on the chip */
+struct snd_hdjmidi* midi_from_chip(struct snd_hdj_chip* chip)
+{
+ struct snd_hdjmidi* umidi=NULL;
+ /* assume only one MIDI device per chip- otherwise expand function parameters */
+ if (list_empty(&chip->midi_list)) {
+ printk(KERN_WARNING"%s() midi_list empty, bailing\n",__FUNCTION__);
+ return NULL;
+ }
+ umidi = list_entry(chip->midi_list.next, struct snd_hdjmidi, list);
+ return umidi;
+}
+
+/*
+ * we need to take care of counter, since disconnection can be called also
+ * many times as well as hdj_probe() returned success.
+ */
+static void hdj_disconnect(struct usb_interface *interface)
+{
+ struct snd_hdj_chip *chip;
+ struct usb_hdjbulk *ubulk;
+ struct list_head *p;
+ struct list_head *next;
+ int chip_index;
+ unsigned long flags;
+ int ifnum;
+ struct usb_host_interface *alts = NULL;
+ alts = &interface->altsetting[0];
+ ifnum = get_iface_desc(alts)->bInterfaceNumber;
+ chip_index = (int)(unsigned long)usb_get_intfdata(interface);
+
+ chip = inc_chip_ref_count(chip_index);
+ if (chip==NULL) {
+ printk(KERN_ERR"%s WARNING: inc_chip_ref_count() returned NULL, bailing!\n",
+ __FUNCTION__);
+ return;
+ }
+
+ /* this operation is for bulk only */
+ if (bulk_intf_num_check(chip,ifnum)==1) {
+ list_for_each_safe(p,next,&chip->bulk_list) {
+ ubulk = list_entry(p, struct usb_hdjbulk, list);
+ if(ubulk != NULL) {
+ if (is_continuous_reader_supported(ubulk->chip)==1) {
+ /* make sure to unblock all readers who are waiting for data, and let them fail with
+ * an error code
+ */
+ spin_lock_irqsave(&ubulk->read_list_lock, flags);
+ signal_all_waiting_readers(&ubulk->open_list);
+ spin_unlock_irqrestore(&ubulk->read_list_lock, flags);
+ }
+ }
+ }
+ }
+
+ /* this decrement balances with the one above */
+ dec_chip_ref_count(chip_index);
+
+ /* this decrement balances with the one in probe */
+ dec_chip_ref_count(chip_index);
+}
+
+#ifdef CONFIG_PM
+
+static int hdj_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ int chip_index = (int)(unsigned long)usb_get_intfdata(intf);
+ struct snd_hdj_chip *chip;
+ struct list_head *p;
+
+ chip = inc_chip_ref_count(chip_index);
+
+ printk(KERN_INFO"hdj_suspend()\n");
+
+ if (!chip) {
+ printk(KERN_WARNING"hdj_suspend() inc_chip_ref_count returned NULL bailing\n");
+ return 0;
+ }
+
+ snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+ if (atomic_inc_return(&chip->num_suspended_intf)==1) {
+ /* this will prevent us from sending down more urbs */
+ atomic_inc(&chip->no_urb_submission);
+
+ if (!list_empty(&chip->midi_list)) {
+ list_for_each(p, &chip->midi_list) {
+ snd_hdjmidi_suspend(p);
+ }
+ }
+
+ if (!list_empty(&chip->bulk_list)) {
+ list_for_each(p, &chip->bulk_list) {
+ snd_hdjbulk_suspend(p);
+ }
+ }
+ hdj_kill_chip_urbs(chip);
+ }
+
+ dec_chip_ref_count(chip_index);
+
+ printk(KERN_INFO"hdj_suspend() EXIT\n");
+
+ return 0;
+}
+
+static int hdj_resume (struct usb_interface *intf)
+{
+ int chip_index = (int)(unsigned long)usb_get_intfdata(intf);
+ struct snd_hdj_chip *chip=NULL;
+ struct list_head *p;
+
+ printk(KERN_INFO"hdj_resume()\n");
+ chip = inc_chip_ref_count(chip_index);
+
+ if (!chip) {
+ printk(KERN_WARNING"hdj_resume() inc_chip_ref_count returned NULL bailing\n");
+ return 0;
+ }
+
+ if (atomic_dec_return(&chip->num_suspended_intf)!=0) {
+ dec_chip_ref_count(chip_index);
+ return 0;
+ }
+
+ /* this will allow us to send down more urbs */
+ atomic_dec(&chip->no_urb_submission);
+
+ if (!list_empty(&chip->midi_list)) {
+ list_for_each(p, &chip->midi_list) {
+ snd_hdjmidi_resume(p);
+ }
+ }
+
+ if (!list_empty(&chip->bulk_list)) {
+ list_for_each(p, &chip->bulk_list) {
+ hdjbulk_resume(p);
+ }
+ }
+
+ /*
+ * ALSA leaves material resumption to user space
+ * we just notify
+ */
+
+ snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+
+ dec_chip_ref_count(chip_index);
+
+ printk(KERN_INFO"hdj_resume() EXIT\n");
+
+ return 0;
+}
+
+static int hdj_reset_resume (struct usb_interface *intf)
+{
+ printk(KERN_WARNING"%s() WARNING- device has been reset\n",__FUNCTION__);
+ /* TODO: this has to be looked into more */
+ hdj_resume(intf);
+ return 0;
+}
+
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) )
+#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) )
+static int hdj_pre_reset(struct usb_interface *intf)
+#else
+static void hdj_pre_reset(struct usb_interface *intf)
+#endif
+{
+ int chip_index = (int)(unsigned long)usb_get_intfdata(intf);
+ struct snd_hdj_chip *chip = NULL;
+ struct list_head *p;
+
+ printk(KERN_INFO"hdj_pre_reset()\n");
+ chip = inc_chip_ref_count(chip_index);
+
+ if (!chip) {
+ printk(KERN_INFO"hdj_pre_reset(): inc_chip_ref_count returned NULL bailing\n");
+#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) )
+ return 0;
+#else
+ return;
+#endif
+ }
+
+ /* This is checked by our wrapper hdjbulk_submit_urb before it calls usb_submit_urb.
+ * open and poll will be forbidden as well */
+ atomic_inc(&chip->no_urb_submission);
+
+ /* ask MIDI to stop I/O */
+ if (!list_empty(&chip->midi_list)) {
+ list_for_each(p, &chip->midi_list) {
+ snd_hdjmidi_pre_reset(p);
+ }
+ }
+
+ /* ask bulk to stop I/O */
+ if (!list_empty(&chip->bulk_list)) {
+ list_for_each(p, &chip->bulk_list) {
+ snd_hdjbulk_pre_reset(p);
+ }
+ }
+
+ hdj_kill_chip_urbs(chip);
+ dec_chip_ref_count(chip_index);
+
+#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) )
+ return 0;
+#endif
+}
+#endif
+
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) )
+#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) )
+static int hdj_post_reset(struct usb_interface *intf)
+#else
+static void hdj_post_reset(struct usb_interface *intf)
+#endif
+{
+ int chip_index = (int)(unsigned long)usb_get_intfdata(intf);
+ struct snd_hdj_chip *chip = NULL;
+ struct list_head *p;
+
+ printk(KERN_INFO"hdj_post_reset()\n");
+ chip = inc_chip_ref_count(chip_index);
+
+ if (!chip) {
+ printk(KERN_INFO"hdj_post_reset(): inc_chip_ref_count returned NULL bailing\n");
+#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) )
+ return 0;
+#else
+ return;
+#endif
+ }
+
+ /* allow I/O to be sent */
+ atomic_dec(&chip->no_urb_submission);
+
+ if (!list_empty(&chip->midi_list)) {
+ list_for_each(p, &chip->midi_list) {
+ snd_hdjmidi_post_reset(p);
+ }
+ }
+
+ if (!list_empty(&chip->bulk_list)) {
+ list_for_each(p, &chip->bulk_list) {
+ snd_hdjbulk_post_reset(p);
+ }
+ }
+
+ dec_chip_ref_count(chip_index);
+
+#if ( LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22) )
+ return 0;
+#endif
+}
+#endif
+#endif
+
+/* crude way to test if device is connected or not */
+int is_device_present(struct snd_hdj_chip* chip)
+{
+ int rc=-ENODEV;
+ u16 devstatus;
+ rc = usb_get_status(chip->dev, USB_RECIP_DEVICE, 0, &devstatus);
+ if (rc >= 0) {
+ rc = (rc > 0 ? 0 : -ENODEV);
+ }
+ return rc;
+}
+
+static int netlink_init(void)
+{
+ int unit, ref_count;
+
+ ref_count = atomic_inc_return(&netlink_ref_count);
+ smp_mb();
+ if (ref_count>1) {
+ return 0;
+ }
+
+ /* Try to allocate a netlink socket minimizing the risk of collision,
+ * by starting at the max unit number and counting down */
+ for (unit=MAX_LINKS-1;unit>MIN_NETLINK_UNIT;unit--) {
+ nl_sk = netlink_kernel_create(
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ &init_net,
+#endif
+ unit,
+ 0,
+ NULL, /* we do not receive messages, only unicast them */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+ NULL,
+#endif
+ THIS_MODULE);
+ if (nl_sk!=NULL) {
+ netlink_unit = unit;
+ return 0;
+ }
+ }
+
+ /* failure, so we must decrement ref count */
+ atomic_dec(&netlink_ref_count);
+ smp_mb();
+
+ return -ENOMEM;
+}
+
+int init_netlink_state(struct snd_hdj_chip* chip)
+{
+ if (netlink_init()!=0) {
+ printk(KERN_WARNING"%s() netlink_init() failed\n",__FUNCTION__);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void netlink_release(void)
+{
+ int ref_count;
+
+ ref_count = atomic_dec_return(&netlink_ref_count);
+ smp_mb();
+ if (ref_count==0) {
+ if (nl_sk != NULL) {
+#if ( LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24) )
+ sock_release(nl_sk->sk_socket);
+#else
+ netlink_kernel_release(nl_sk);
+#endif
+ nl_sk = NULL;
+ netlink_unit = NETLINK_UNIT_INVALID_VALUE;
+ } else {
+ printk(KERN_INFO"%s() already deleted\n",__FUNCTION__);
+ }
+ }
+}
+
+void uninit_netlink_state(struct snd_hdj_chip* chip)
+{
+ unregister_for_netlink(chip,NULL);
+ netlink_release();
+}
+
+/* This enter netlink message */
+static struct sk_buff *netlink_make_reply(int target_pid, int seq, int type, int done,
+ int multi, void *payload, int size)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ int len = NLMSG_SPACE(size);
+ void *data;
+ int flags = multi ? NLM_F_MULTI : 0;
+ int t = done ? NLMSG_DONE : type;
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ return NULL;
+ }
+
+ nlh = NLMSG_PUT(skb, target_pid, seq, t, size);
+ nlh->nlmsg_flags = flags;
+ data = NLMSG_DATA(nlh);
+ if (size > 0) {
+ memcpy(data, payload, size);
+ }
+ return skb;
+
+nlmsg_failure: /* Used by NLMSG_PUT */
+ if (skb) {
+ kfree_skb(skb);
+ }
+ return NULL;
+}
+
+int register_for_netlink(struct snd_hdj_chip* chip,
+ struct file* file,
+ void* context,
+ u8 compat_mode)
+{
+ struct netlink_list * netlink_item = NULL;
+ int tgid = current->tgid;
+ struct list_head *p;
+ struct list_head *next;
+
+ down(&chip->netlink_list_mutex);
+ /* verify if the process was already registered */
+ if (!list_empty(&chip->netlink_registered_processes)) {
+ list_for_each_safe(p,next,&chip->netlink_registered_processes) {
+ netlink_item = list_entry(p, struct netlink_list, list);
+ if (netlink_item->pid == tgid) {
+ printk(KERN_ERR"%s() already registered.\n",__FUNCTION__);
+ up(&chip->netlink_list_mutex);
+ return -EINVAL;
+ }
+ }
+ }
+
+ netlink_item = zero_alloc(sizeof(struct netlink_list),GFP_KERNEL);
+ if (netlink_item == NULL) {
+ printk(KERN_ERR"%s() failed to allocate memory.\n",__FUNCTION__);
+ return -ENOMEM;
+ }
+
+ netlink_item->pid = tgid;
+ netlink_item->context = context;
+ netlink_item->file = file;
+ netlink_item->compat_mode = compat_mode;
+
+ /* add the process information to the list */
+ list_add_tail(&netlink_item->list,&chip->netlink_registered_processes);
+ up(&chip->netlink_list_mutex);
+
+ return 0;
+}
+
+
+/* Note: if file==NULL unregister all clients */
+int unregister_for_netlink(struct snd_hdj_chip* chip, struct file* file)
+{
+ struct sk_buff *skb;
+ struct netlink_list * netlink_item = NULL;
+ struct list_head *p;
+ struct list_head *next;
+ /*int tgid = current->tgid;*/
+ int item_freed = 0;
+ /*printk(KERN_INFO"%s() received pid:%d, file:%p\n",__FUNCTION__,tgid,file);*/
+ down(&chip->netlink_list_mutex);
+ /* verify if the process was already registered */
+ if (!list_empty(&chip->netlink_registered_processes)) {
+ list_for_each_safe(p,next,&chip->netlink_registered_processes) {
+ netlink_item = list_entry(p, struct netlink_list, list);
+ if (netlink_item->file == file || file == NULL) {
+ /*printk(KERN_INFO"unregister_for_netlink(): unregistering TGID: %d, file:%p\n",
+ netlink_item->pid,
+ file);*/
+ /* send a 0 length buffer- this will unblock usermode clients */
+ skb = netlink_make_reply(netlink_item->pid, 0, 0, 1, 0, NULL, 0);
+ if (skb != NULL && nl_sk!=NULL) {
+ netlink_unicast(nl_sk, skb, netlink_item->pid, MSG_DONTWAIT);
+ }
+
+ list_del(p);
+ kfree(netlink_item);
+
+ item_freed = 1;
+ }
+ }
+ }
+ up(&chip->netlink_list_mutex);
+
+ if (item_freed == 0 && file != NULL) {
+ /*printk(KERN_INFO"%s() returning error\n",__FUNCTION__);*/
+ return -EINVAL;
+ } else {
+ /*printk(KERN_INFO"%s() returning success\n",__FUNCTION__);*/
+ return 0;
+ }
+}
+
+/* This sends a raw message over netlink to listeners (in usermode) */
+static int netlink_send_raw_msg(struct snd_hdj_chip* chip,
+ struct netlink_msg_header* msg,
+ int len
+ #ifdef CONFIG_COMPAT
+ ,struct netlink_msg_header32* msg_compat,
+ int len_compat
+ #endif
+ )
+{
+ struct sk_buff *skb;
+ struct list_head *p;
+ struct list_head *next;
+ struct netlink_list * netlink_item = NULL;
+ int ret = 0;
+
+ if (nl_sk == NULL) {
+ printk(KERN_INFO"%s() Invalid Socket!\n",__FUNCTION__);
+ return -EINVAL;
+ }
+
+ down(&chip->netlink_list_mutex);
+ /* verify if the process was already registered */
+ if (!list_empty(&chip->netlink_registered_processes)) {
+ list_for_each_safe(p,next,&chip->netlink_registered_processes) {
+ netlink_item = list_entry(p, struct netlink_list, list);
+
+ /*printk(KERN_INFO"%s() making message...: tgid: %d pid: %d\n",
+ __FUNCTION__,current->tgid, current->pid);*/
+#ifdef CONFIG_COMPAT
+ if (netlink_item->compat_mode==1) {
+ msg_compat->context = (compat_ulong_t)netlink_item->context;
+ skb = netlink_make_reply(netlink_item->pid, 0, 0, 1, 0,
+ msg_compat, len_compat);
+ } else {
+ msg->context = netlink_item->context;
+ skb = netlink_make_reply(netlink_item->pid, 0, 0, 1, 0, msg, len);
+ }
+#else
+ msg->context = netlink_item->context;
+ skb = netlink_make_reply(netlink_item->pid, 0, 0, 1, 0, msg, len);
+#endif
+
+ if (skb != NULL && nl_sk!=NULL) {
+ ret = netlink_unicast(nl_sk, skb, netlink_item->pid, MSG_DONTWAIT);
+ } else {
+ ret = -EINVAL;
+ }
+ }
+ }
+ up(&chip->netlink_list_mutex);
+
+ return ret;
+}
+
+/* This creates a buffer in which a netlink header and the message parameter, and request that
+ * it be sent over netlink to listeners (in usermode) */
+static int netlink_send_msg(struct snd_hdj_chip* chip,
+ unsigned long msg_id,
+ void* data,
+ unsigned long data_len
+ #ifdef CONFIG_COMPAT
+ ,void* data_compat,
+ unsigned long data_len_compat
+ #endif
+ )
+{
+ struct netlink_msg_header *msg=NULL;
+ int msg_size, rc = 0;
+#ifdef CONFIG_COMPAT
+ struct netlink_msg_header32 *msg_compat=NULL;
+ int msg_size_compat;
+#endif
+
+ msg_size = sizeof(struct netlink_msg_header)+data_len;
+ msg = (struct netlink_msg_header *)zero_alloc(msg_size, GFP_KERNEL);
+#ifdef CONFIG_COMPAT
+ msg_size_compat = sizeof(struct netlink_msg_header32)+data_len_compat;
+ msg_compat = (struct netlink_msg_header32 *)zero_alloc(msg_size_compat, GFP_KERNEL);
+#endif
+
+#ifdef CONFIG_COMPAT
+ if (msg!=NULL && msg_compat!=NULL) {
+#else
+ if (msg!=NULL) {
+#endif
+ msg->msg_magic_number = MAGIC_NUM;
+ msg->header_size = sizeof(struct netlink_msg_header);
+ msg->msg_id = msg_id;
+ msg->bytes_to_follow = data_len;
+ memcpy((char*)msg+sizeof(struct netlink_msg_header),data,data_len);
+
+#ifdef CONFIG_COMPAT
+ msg_compat->msg_magic_number = (compat_ulong_t)MAGIC_NUM;
+ msg_compat->header_size = (compat_ulong_t)sizeof(struct netlink_msg_header32);
+ msg_compat->msg_id = (compat_ulong_t)msg_id;
+ msg_compat->bytes_to_follow = (compat_ulong_t)data_len_compat;
+ memcpy((char*)msg_compat+sizeof(struct netlink_msg_header32),
+ data_compat,data_len_compat);
+#endif
+ /* will send async, so we can free the buffer */
+ rc = netlink_send_raw_msg(chip,
+ msg,
+ msg_size
+ #ifdef CONFIG_COMPAT
+ ,msg_compat,
+ msg_size_compat
+ #endif
+ );
+ }
+
+#ifdef CONFIG_COMPAT
+ if (msg!=NULL) {
+ kfree(msg);
+ }
+ if (msg_compat!=NULL) {
+ kfree(msg_compat);
+ }
+#else
+ if (msg!=NULL) {
+ kfree(msg);
+ }
+#endif
+
+ return rc;
+}
+
+/* This sends a control change to all listeners (over netlink in usermode), and formats
+ * the control change data into a control change message */
+int send_control_change_over_netlink(struct snd_hdj_chip* chip,
+ unsigned long product_code,
+ unsigned long control_id,
+ unsigned long control_value)
+{
+ struct control_change_data control_msg;
+#ifdef CONFIG_COMPAT
+ struct control_change_data32 control_msg_compat;
+#endif
+ int rc=0;
+
+ control_msg.product_code = product_code;
+ control_msg.control_id = control_id;
+ control_msg.control_value = control_value;
+ memcpy(&control_msg.location_id,chip->usb_device_path,LOCATION_ID_LEN);
+
+#ifdef CONFIG_COMPAT
+ control_msg_compat.product_code = (compat_ulong_t)product_code;
+ control_msg_compat.control_id = (compat_ulong_t)control_id;
+ control_msg_compat.control_value = (compat_ulong_t)control_value;
+ memcpy(&control_msg_compat.location_id,chip->usb_device_path,LOCATION_ID_LEN);
+#endif
+ rc = netlink_send_msg(chip,
+ MSG_CONTROL_CHANGE,
+ &control_msg,
+ sizeof(control_msg)
+ #ifdef CONFIG_COMPAT
+ ,&control_msg_compat,
+ sizeof(control_msg_compat)
+ #endif
+ );
+
+ return rc;
+}
+
+/* MARK: PRODCHANGE */
+/* just does a printk of the product name and IDs, and driver version */
+void dump_product_name_to_console(struct snd_hdj_chip* chip,
+ u8 output_bulk_info,
+ u8 output_midi_info)
+{
+ if (output_midi_info!=0) {
+ printk(KERN_INFO"MIDI state successfully created, driver version:%x\n",
+ driver_version);
+ }
+ if (output_bulk_info!=0) {
+ printk(KERN_INFO"Bulk state sucessfully created, driver version:%x\n",
+ driver_version);
+ }
+ if (chip->product_code==DJCONSOLE_PRODUCT_CODE) {
+ printk(KERN_INFO"DJ Console, vid:%x, pid:%x\n",
+ USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+ } else if (chip->product_code==DJCONSOLE2_PRODUCT_CODE) {
+ printk(KERN_INFO"DJ Console Mk2, vid:%x, pid:%x\n",
+ USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+ } else if (chip->product_code==DJCONSOLERMX_PRODUCT_CODE) {
+ printk(KERN_INFO"DJ Console Rmx, vid:%x, pid:%x\n",
+ USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+ } else if (chip->product_code==DJCONTROLSTEEL_PRODUCT_CODE) {
+ printk(KERN_INFO"DJ Control Steel, vid:%x, pid:%x\n",
+ USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+ } else if (chip->product_code==DJCONTROLLER_PRODUCT_CODE) {
+ printk(KERN_INFO"DJ Control MP3, vid:%x, pid:%x\n",
+ USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+ } else {
+ printk(KERN_WARNING"%s unknown product:%d, vid:%x, pid:%x\n",
+ __FUNCTION__,chip->product_code,
+ USB_ID_VENDOR(chip->usb_id),USB_ID_PRODUCT(chip->usb_id));
+ }
+}
+
+static int __init usb_hdj_init(void)
+{
+ int result;
+
+ /* initialize MIDI */
+ midi_init();
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&hdj_driver);
+ if (result)
+ printk(KERN_ERR"usb_hdj_init(): usb_register failed. Error number %d\n", result);
+
+ return result;
+}
+
+static void __exit usb_hdj_exit(void)
+{
+ /* cleanup MIDI */
+ midi_cleanup();
+
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&hdj_driver);
+}
+
+module_init(usb_hdj_init);
+module_exit(usb_hdj_exit);
+MODULE_DESCRIPTION("DJ Series Kernel Module");
+MODULE_SUPPORTED_DEVICE("DJ Series Devices");
+MODULE_AUTHOR("Philip Lukidis <plukidis@guillemot.com>");
+MODULE_LICENSE("GPL");