2013-06-22 00:14:46 +02:00
/*
* Linux driver for M2Tech hiFace compatible devices
*
* Copyright 2012 - 2013 ( C ) M2TECH S . r . l and Amarula Solutions B . V .
*
* Authors : Michael Trimarchi < michael @ amarulasolutions . com >
* Antonio Ospite < ao2 @ amarulasolutions . com >
*
* The driver is based on the work done in TerraTec DMX 6F ire USB
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <sound/initval.h>
# include "chip.h"
# include "pcm.h"
MODULE_AUTHOR ( " Michael Trimarchi <michael@amarulasolutions.com> " ) ;
MODULE_AUTHOR ( " Antonio Ospite <ao2@amarulasolutions.com> " ) ;
MODULE_DESCRIPTION ( " M2Tech hiFace USB-SPDIF audio driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_SUPPORTED_DEVICE ( " {{M2Tech,Young}, "
" {M2Tech,hiFace}, "
" {M2Tech,North Star}, "
" {M2Tech,W4S Young}, "
" {M2Tech,Corrson}, "
" {M2Tech,AUDIA}, "
" {M2Tech,SL Audio}, "
" {M2Tech,Empirical}, "
" {M2Tech,Rockna}, "
" {M2Tech,Pathos}, "
" {M2Tech,Metronome}, "
" {M2Tech,CAD}, "
" {M2Tech,Audio Esclusive}, "
" {M2Tech,Rotel}, "
" {M2Tech,Eeaudio}, "
" {The Chord Company,CHORD}, "
" {AVA Group A/S,Vitus}} " ) ;
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ; /* Index 0-max */
static char * id [ SNDRV_CARDS ] = SNDRV_DEFAULT_STR ; /* Id for card */
static bool enable [ SNDRV_CARDS ] = SNDRV_DEFAULT_ENABLE_PNP ; /* Enable this card */
# define DRIVER_NAME "snd-usb-hiface"
# define CARD_NAME "hiFace"
module_param_array ( index , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( index , " Index value for " CARD_NAME " soundcard. " ) ;
module_param_array ( id , charp , NULL , 0444 ) ;
MODULE_PARM_DESC ( id , " ID string for " CARD_NAME " soundcard. " ) ;
module_param_array ( enable , bool , NULL , 0444 ) ;
MODULE_PARM_DESC ( enable , " Enable " CARD_NAME " soundcard. " ) ;
static DEFINE_MUTEX ( register_mutex ) ;
struct hiface_vendor_quirk {
const char * device_name ;
u8 extra_freq ;
} ;
2014-01-29 14:22:20 +01:00
static int hiface_chip_create ( struct usb_interface * intf ,
struct usb_device * device , int idx ,
2013-06-22 00:14:46 +02:00
const struct hiface_vendor_quirk * quirk ,
struct hiface_chip * * rchip )
{
struct snd_card * card = NULL ;
struct hiface_chip * chip ;
int ret ;
int len ;
* rchip = NULL ;
/* if we are here, card can be registered in alsa. */
2014-01-29 14:22:20 +01:00
ret = snd_card_new ( & intf - > dev , index [ idx ] , id [ idx ] , THIS_MODULE ,
sizeof ( * chip ) , & card ) ;
2013-06-22 00:14:46 +02:00
if ( ret < 0 ) {
dev_err ( & device - > dev , " cannot create alsa card. \n " ) ;
return ret ;
}
strlcpy ( card - > driver , DRIVER_NAME , sizeof ( card - > driver ) ) ;
if ( quirk & & quirk - > device_name )
strlcpy ( card - > shortname , quirk - > device_name , sizeof ( card - > shortname ) ) ;
else
strlcpy ( card - > shortname , " M2Tech generic audio " , sizeof ( card - > shortname ) ) ;
strlcat ( card - > longname , card - > shortname , sizeof ( card - > longname ) ) ;
len = strlcat ( card - > longname , " at " , sizeof ( card - > longname ) ) ;
if ( len < sizeof ( card - > longname ) )
usb_make_path ( device , card - > longname + len ,
sizeof ( card - > longname ) - len ) ;
chip = card - > private_data ;
chip - > dev = device ;
chip - > card = card ;
* rchip = chip ;
return 0 ;
}
static int hiface_chip_probe ( struct usb_interface * intf ,
const struct usb_device_id * usb_id )
{
const struct hiface_vendor_quirk * quirk = ( struct hiface_vendor_quirk * ) usb_id - > driver_info ;
int ret ;
int i ;
struct hiface_chip * chip ;
struct usb_device * device = interface_to_usbdev ( intf ) ;
ret = usb_set_interface ( device , 0 , 0 ) ;
if ( ret ! = 0 ) {
dev_err ( & device - > dev , " can't set first interface for " CARD_NAME " device. \n " ) ;
return - EIO ;
}
/* check whether the card is already registered */
chip = NULL ;
mutex_lock ( & register_mutex ) ;
for ( i = 0 ; i < SNDRV_CARDS ; i + + )
if ( enable [ i ] )
break ;
if ( i > = SNDRV_CARDS ) {
dev_err ( & device - > dev , " no available " CARD_NAME " audio device \n " ) ;
ret = - ENODEV ;
goto err ;
}
2014-01-29 14:22:20 +01:00
ret = hiface_chip_create ( intf , device , i , quirk , & chip ) ;
2013-06-22 00:14:46 +02:00
if ( ret < 0 )
goto err ;
ret = hiface_pcm_init ( chip , quirk ? quirk - > extra_freq : 0 ) ;
if ( ret < 0 )
goto err_chip_destroy ;
ret = snd_card_register ( chip - > card ) ;
if ( ret < 0 ) {
dev_err ( & device - > dev , " cannot register " CARD_NAME " card \n " ) ;
goto err_chip_destroy ;
}
mutex_unlock ( & register_mutex ) ;
usb_set_intfdata ( intf , chip ) ;
return 0 ;
err_chip_destroy :
snd_card_free ( chip - > card ) ;
err :
mutex_unlock ( & register_mutex ) ;
return ret ;
}
static void hiface_chip_disconnect ( struct usb_interface * intf )
{
struct hiface_chip * chip ;
struct snd_card * card ;
chip = usb_get_intfdata ( intf ) ;
if ( ! chip )
return ;
card = chip - > card ;
/* Make sure that the userspace cannot create new request */
snd_card_disconnect ( card ) ;
hiface_pcm_abort ( chip ) ;
snd_card_free_when_closed ( card ) ;
}
static const struct usb_device_id device_table [ ] = {
{
USB_DEVICE ( 0x04b4 , 0x0384 ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Young " ,
. extra_freq = 1 ,
}
} ,
{
USB_DEVICE ( 0x04b4 , 0x930b ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " hiFace " ,
}
} ,
{
USB_DEVICE ( 0x04b4 , 0x931b ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " North Star " ,
}
} ,
{
USB_DEVICE ( 0x04b4 , 0x931c ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " W4S Young " ,
}
} ,
{
USB_DEVICE ( 0x04b4 , 0x931d ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Corrson " ,
}
} ,
{
USB_DEVICE ( 0x04b4 , 0x931e ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " AUDIA " ,
}
} ,
{
USB_DEVICE ( 0x04b4 , 0x931f ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " SL Audio " ,
}
} ,
{
USB_DEVICE ( 0x04b4 , 0x9320 ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Empirical " ,
}
} ,
{
USB_DEVICE ( 0x04b4 , 0x9321 ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Rockna " ,
}
} ,
{
USB_DEVICE ( 0x249c , 0x9001 ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Pathos " ,
}
} ,
{
USB_DEVICE ( 0x249c , 0x9002 ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Metronome " ,
}
} ,
{
USB_DEVICE ( 0x249c , 0x9006 ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " CAD " ,
}
} ,
{
USB_DEVICE ( 0x249c , 0x9008 ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Audio Esclusive " ,
}
} ,
{
USB_DEVICE ( 0x249c , 0x931c ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Rotel " ,
}
} ,
{
USB_DEVICE ( 0x249c , 0x932c ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Eeaudio " ,
}
} ,
{
USB_DEVICE ( 0x245f , 0x931c ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " CHORD " ,
}
} ,
{
USB_DEVICE ( 0x25c6 , 0x9002 ) ,
. driver_info = ( unsigned long ) & ( const struct hiface_vendor_quirk ) {
. device_name = " Vitus " ,
}
} ,
{ }
} ;
MODULE_DEVICE_TABLE ( usb , device_table ) ;
static struct usb_driver hiface_usb_driver = {
. name = DRIVER_NAME ,
. probe = hiface_chip_probe ,
. disconnect = hiface_chip_disconnect ,
. id_table = device_table ,
} ;
module_usb_driver ( hiface_usb_driver ) ;