Commit b6ad4dc2 authored by Paul Mackerras's avatar Paul Mackerras

Merge samba.org:/home/paulus/kernel/linux-2.5

into samba.org:/home/paulus/kernel/for-linus-ppc
parents 36573454 8a257dca
......@@ -1081,6 +1081,11 @@ D: Made support for modules, ramdisk, generic-serial, etc. optional.
D: Transformed old user space bdflush into 1st kernel thread - kflushd.
D: Many other patches, documentation files, mini kernels, utilities, ...
N: Masanori GOTO
E: gotom@debian.or.jp
D: Workbit NinjaSCSI-32Bi/UDE driver
S: Japan
N: John E. Gotts
E: jgotts@linuxsavvy.com
D: kernel hacker
......@@ -3338,6 +3343,12 @@ S: POB 1822
S: Socorro NM, 87801
S: USA
N: Hiroshi YOKOTA
E: yokota@netlab.is.tsukuba.ac.jp
D: Workbit NinjaSCSI-3/32Bi PCMCIA driver
D: Workbit NinjaSCSI-32Bi/UDE driver
S: Japan
N: Eric Youngdale
E: eric@andante.org
W: http://www.andante.org
......
......@@ -1166,6 +1166,14 @@ M: yokota@netlab.is.tsukuba.ac.jp
W: http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/
S: Maintained
NINJA SCSI-32Bi/UDE PCI/CARDBUS SCSI HOST ADAPTER DRIVER
P: GOTO Masanori
M: gotom@debian.or.jp
P: YOKOTA Hiroshi
M: yokota@netlab.is.tsukuba.ac.jp
W: http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/
S: Maintained
NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility)
P: Eberhard Moenkeberg
M: emoenke@gwdg.de
......
#
# Makefile for Bluetooth HCI device drivers.
# Makefile for the Linux Bluetooth HCI device drivers.
#
obj-$(CONFIG_BLUEZ_HCIUSB) += hci_usb.o
......
......@@ -2617,6 +2617,7 @@
10fc I-O Data Device, Inc.
# What's in the cardbus end of a Sony ACR-A01 card, comes with newer Vaio CD-RW drives
0003 Cardbus IDE Controller
0005 Cardbus SCSI CBSC II
10fd Soyo Computer, Inc
10fe Fast Multimedia AG
10ff NCube
......@@ -3027,6 +3028,12 @@
1144 Cincinnati Milacron
0001 Noservo controller
1145 Workbit Corporation
f007 NinjaSCSI-32 KME
8007 NinjaSCSI-32 Workbit
f010 NinjaSCSI-32 Workbit
f012 NinjaSCSI-32 Logitec
f013 NinjaSCSI-32 Logitec
f015 NinjaSCSI-32 Melco
1146 Force Computers
1147 Interface Corp
1148 Syskonnect (Schneider & Koch)
......
......@@ -788,6 +788,16 @@ CONFIG_SCSI_NCR53C8XX_PQS_PDS
The common answer here is N, but answering Y is safe.
CONFIG_SCSI_NSP32
This is support for the Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus
SCSI host adapter. Please read the SCSI-HOWTO, available from
<http://www.linuxdoc.org/docs.html#howto>.
If you want to compile this as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/modules.txt>. The module
will be called nsp32.o.
CONFIG_SCSI_IBMMCA
This is support for the IBM SCSI adapter found in many of the PS/2
series computers. These machines have an MCA bus, so you need to
......
......@@ -213,6 +213,7 @@ fi
if [ "$CONFIG_X86" = "y" -a "$CONFIG_ISA" = "y" ]; then
dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
fi
dep_tristate 'Workbit NinjaSCSI-32Bi/UDE support' CONFIG_SCSI_NSP32 $CONFIG_SCSI
#
# Note - this is a very special 'host' adapter that simulates the presence of some disks.
# It can come in very handy for troubleshooting. Anyone else is welcome to use it - all
......
......@@ -111,6 +111,7 @@ obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
obj-$(CONFIG_SCSI_FCAL) += fcal.o
obj-$(CONFIG_SCSI_CPQFCTS) += cpqfc.o
obj-$(CONFIG_SCSI_LASI700) += lasi700.o 53c700.o
obj-$(CONFIG_SCSI_NSP32) += nsp32.o
obj-$(CONFIG_ARCH_ACORN) += ../acorn/scsi/
......
/*
* NinjaSCSI-32Bi Cardbus, NinjaSCSI-32UDE PCI/CardBus SCSI driver
* Copyright (C) 2001, 2002
* YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
* GOTO Masanori <gotom@debian.or.jp>
*
* 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, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/major.h>
#include <linux/blk.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/io.h>
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi.h>
#include "nsp32.h"
static int trans_mode = 0; /* default: BIOS */
static int auto_param = 0; /* default: ON */
MODULE_PARM(trans_mode, "i");
MODULE_PARM(auto_param, "i");
MODULE_PARM_DESC(trans_mode, "transfer mode (0: BIOS 1: Async 2: Ultra20M");
MODULE_PARM_DESC(auto_param, "AutoParameter mode (0: ON 1: OFF)");
#define ASYNC_MODE 1
#define ULTRA20M_MODE 2
MODULE_AUTHOR("YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>, GOTO Masanori <gotom@debian.or.jp>");
MODULE_DESCRIPTION("Workbit NinjaSCSI-32Bi/UDE PCI/CardBus SCSI host bus adapter module");
MODULE_LICENSE("GPL");
static const char *nsp32_release_version = "1.0";
/*
* structure for DMA/Scatter Gather list
*/
#define AUTOPARAM_SIZE (sizeof(int)*0x15) /* 4x15H = 0x60 */
#define NSP_SG_SIZE SG_ALL
#define NSP32_SG_END_SGT 0x80000000 /* Last SGT marker */
#define NSP32_SG_CNT_MASK 0x1FFFF
struct nsp32_sgtable {
unsigned long addr; /* transfer address */
unsigned long len; /* transfer length.
Bit (24-32) is for SGT_END */
};
struct nsp32_sglun {
struct nsp32_sgtable sgt[NSP_SG_SIZE+1]; /* SG table */
};
/*
* host data structure
*/
/* message in/out buffer */
#define MSGOUTBUF_MAX 13 /* 13 is ok ? */
#define MSGINBUF_MAX 13
/* flag for trans_method */
#define NSP32_TRANSFER_BUSMASTER BIT(0)
#define NSP32_TRANSFER_MMIO BIT(1) /* Not supported yet */
#define NSP32_TRANSFER_PIO BIT(2) /* Not supported yet */
/*
* SCSI TARGET/LUN definition
*/
#define NSP32_HOST_SCSIID 7 /* SCSI initiator is everytime defined as 7 */
#define MAX_TARGET 8
#define MAX_LUN 8 /* XXX: In SPI3, max number of LUN is 64. */
/*
* structure for synchronous transfer negotiation data
*/
#define SYNC_NOT_YET 0
#define SYNC_OK 1
#define SYNC_NG 2
struct nsp32_sync_table {
unsigned char period_num; /* period number */
unsigned char ackwidth; /* ack width designated by period */
unsigned char start_period; /* search range - start period */
unsigned char end_period; /* search range - end period */
};
/*
* structure for target device static data
*/
/* flag for nsp32_target.sync_flag */
#define SDTR_INITIATOR BIT(0) /* sending SDTR from initiator */
#define SDTR_TARGET BIT(1) /* sending SDTR from target */
#define SDTR_DONE BIT(2) /* exchanging SDTR has been processed */
/* syncronous period value for nsp32_target.config_max */
#define FAST5M 0x32
#define FAST10M 0x19
#define ULTRA20M 0x0c
/* flag for nsp32_target.{sync_offset}, period */
#define ASYNC_OFFSET 0 /* asynchronous transfer */
#define SYNC_OFFSET 0xf /* synchronous transfer max offset */
/* syncreg:
07 06 05 04 03 02 01 00
---PERIOD-- ---OFFSET-- */
#define TO_SYNCREG(period, offset) (period << 4 | offset)
struct nsp32_target {
unsigned char syncreg; /* value for SYNCREG */
unsigned char ackwidth; /* value for ACKWIDTH */
unsigned char offset; /* sync offset (0-15) */
int sync_flag; /* SDTR_*, 0 */
int limit_entry; /* max speed limit entry designated
by EEPROM configuration */
};
typedef struct _nsp32_hw_data {
int IrqNumber;
int BaseAddress;
int NumAddress;
#define NSP32_MMIO_OFFSET 0x0800
unsigned long MmioAddress;
unsigned long length;
Scsi_Cmnd *CurrentSC;
struct pci_dev *Pci;
const struct pci_device_id *pci_devid;
struct Scsi_Host *Host;
spinlock_t Lock;
char info_str[100];
/* allocated memory region */
struct nsp32_lunt *lunt_list; /* kmalloc region for lunt */
struct nsp32_sglun *sg_list; /* sglist virtual address */
dma_addr_t sgaddr; /* physical address of hw_sg_table */
unsigned char *autoparam; /* auto parameter transfer region */
dma_addr_t apaddr; /* physical address of autoparam */
int cur_entry; /* current sgt entry */
/* target/LUN */
struct nsp32_lunt *curlunt; /* Current connected LUN table */
struct nsp32_lunt *lunt[MAX_TARGET][MAX_LUN]; /* All LUN table */
struct nsp32_target *curtarget; /* Current connected SCSI ID */
struct nsp32_target target[MAX_TARGET]; /* SCSI ID */
int pid; /* Current connected target ID */
int plun; /* Current connected target LUN */
/* behavior setting parameters */
int trans_method; /* transfer method flag */
int resettime; /* Reset time */
int clock; /* clock dividing flag */
struct nsp32_sync_table *synct; /* sync_table determined by clock */
int syncnum; /* the max number of synct element */
/* message buffer */
unsigned char msgoutbuf[MSGOUTBUF_MAX]; /* msgout buffer */
char msgoutlen; /* msgoutbuf length */
unsigned char msginbuf[MSGINBUF_MAX]; /* megin buffer */
char msginlen; /* msginbuf length */
} nsp32_hw_data;
static nsp32_hw_data nsp32_data_base; /* probe <-> detect glue */
/*
* TIME definition
*/
#define RESET_HOLD_TIME 10000 /* reset time in us (SCSI-2 says the
minimum is 25us) */
#define SEL_TIMEOUT_TIME 10000 /* 250ms defined in SCSI specification
(25.6us/1unit) */
#define ARBIT_TIMEOUT_TIME 100 /* 100us */
#define REQSACK_TIMEOUT_TIME 10000 /* max wait time for REQ/SACK assertion
or negation, 10000us == 10ms */
/*
* structure for connected LUN dynamic data
*
* Note: Currently tagged queuing is disabled, each nsp32_lunt holds
* one SCSI command and one state.
*/
#define DISCPRIV_OK BIT(0) /* DISCPRIV Enable mode */
#define MSGIN03 BIT(1) /* Auto Msg In 03 Flag */
struct nsp32_lunt {
Scsi_Cmnd *SCpnt; /* Current Handling Scsi_Cmnd */
unsigned long save_datp; /* Save Data Pointer - saved position from initial address */
int msgin03; /* auto msg in 03 flag */
unsigned int sg_num; /* Total number of SG entries */
int cur_entry; /* Current SG entry number */
struct nsp32_sglun *sglun; /* sg table per lun */
long sglun_paddr; /* sglun physical address */
};
/*
* Period/AckWidth speed conversion table
*
* Note: This period/ackwidth speed table must be in descending order.
*/
static struct nsp32_sync_table nsp32_sync_table_40M[] = {
/* {PNo, AW, SP, EP} Speed(MB/s) Period AckWidth */
{0x1, 0, 0x0c, 0x0c}, /* 20.0 : 50ns, 25ns */
{0x2, 0, 0x0d, 0x18}, /* 13.3 : 75ns, 25ns */
{0x3, 1, 0x19, 0x19}, /* 10.0 : 100ns, 50ns */
{0x4, 1, 0x1a, 0x1f}, /* 8.0 : 125ns, 50ns */
{0x5, 2, 0x20, 0x25}, /* 6.7 : 150ns, 75ns */
{0x6, 2, 0x26, 0x31}, /* 5.7 : 175ns, 75ns */
{0x7, 3, 0x32, 0x32}, /* 5.0 : 200ns, 100ns */
{0x8, 3, 0x33, 0x38}, /* 4.4 : 225ns, 100ns */
{0x9, 3, 0x39, 0x3e}, /* 4.0 : 250ns, 100ns */
};
static const int nsp32_table_40M_num =
sizeof(nsp32_sync_table_40M)/sizeof(struct nsp32_sync_table);
static struct nsp32_sync_table nsp32_sync_table_20M[] = {
{0x1, 0, 0x19, 0x19}, /* 10.0 : 100ns, 50ns */
{0x2, 0, 0x1a, 0x25}, /* 6.7 : 150ns, 50ns */
{0x3, 1, 0x26, 0x32}, /* 5.0 : 200ns, 100ns */
{0x4, 1, 0x33, 0x3e}, /* 4.0 : 250ns, 100ns */
{0x5, 2, 0x3f, 0x4b}, /* 3.3 : 300ns, 150ns */
{0x6, 2, 0x4c, 0x57}, /* 2.8 : 350ns, 150ns */
{0x7, 3, 0x58, 0x64}, /* 2.5 : 400ns, 200ns */
{0x8, 3, 0x65, 0x70}, /* 2.2 : 450ns, 200ns */
{0x9, 3, 0x71, 0x7d}, /* 2.0 : 500ns, 200ns */
};
static const int nsp32_table_20M_num =
sizeof(nsp32_sync_table_20M)/sizeof(struct nsp32_sync_table);
static struct nsp32_sync_table nsp32_sync_table_pci[] = {
{0x1, 0, 0x0c, 0x0f}, /* 16.6 : 60ns, 30ns */
{0x2, 0, 0x10, 0x16}, /* 11.1 : 90ns, 30ns */
{0x3, 1, 0x17, 0x1e}, /* 8.3 : 120ns, 60ns */
{0x4, 1, 0x1f, 0x25}, /* 6.7 : 150ns, 60ns */
{0x5, 2, 0x26, 0x2d}, /* 5.6 : 180ns, 90ns */
{0x6, 2, 0x2e, 0x34}, /* 4.8 : 210ns, 90ns */
{0x7, 3, 0x35, 0x3c}, /* 4.2 : 240ns, 120ns */
{0x8, 3, 0x3d, 0x43}, /* 3.7 : 270ns, 120ns */
{0x9, 3, 0x44, 0x4b}, /* 3.3 : 300ns, 120ns */
};
static const int nsp32_table_pci_num =
sizeof(nsp32_sync_table_pci)/sizeof(struct nsp32_sync_table);
/*
* function declaration
*/
static int nsp32_detect(Scsi_Host_Template *);
static int nsp32_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
static const char *nsp32_info(struct Scsi_Host *);
static int nsp32_eh_abort(Scsi_Cmnd *);
static int nsp32_eh_bus_reset(Scsi_Cmnd *);
static int nsp32_eh_host_reset(Scsi_Cmnd *);
static int nsp32_reset(Scsi_Cmnd *, unsigned int);
static int nsp32_release(struct Scsi_Host *);
static int nsp32_proc_info(char *, char **, off_t, int, int, int);
static int __devinit nsp32_probe(struct pci_dev *, const struct pci_device_id *);
static void __devexit nsp32_remove(struct pci_dev *);
static int __init init_nsp32(void);
static void __exit exit_nsp32(void);
static void nsp32_message(char *, int, char *, char *, ...);
static void nsp32_dmessage(char *, int, int, char *, ...);
static void nsp32_build_identify(nsp32_hw_data *, Scsi_Cmnd *);
static void nsp32_build_sdtr(nsp32_hw_data *, unsigned char, unsigned char);
static void nsp32_build_nop(nsp32_hw_data *);
static void nsp32_build_reject(nsp32_hw_data *);
static int nsp32hw_start_selection(Scsi_Cmnd *, nsp32_hw_data *);
static int nsp32_selection_autoscsi(Scsi_Cmnd *, nsp32_hw_data *);
static int nsp32_reselection(nsp32_hw_data *, unsigned char);
static int nsp32hw_setup_sg_table(Scsi_Cmnd *, nsp32_hw_data *);
static int nsp32hw_init(struct Scsi_Host *);
static void nsp32_scsi_done(nsp32_hw_data *, Scsi_Cmnd *);
static int nsp32_busfree_occur(nsp32_hw_data *, unsigned short);
static void nsp32_adjust_busfree(nsp32_hw_data *, unsigned int);
static void nsp32_msgout_occur(nsp32_hw_data *);
static void nsp32_restart_autoscsi(nsp32_hw_data *, unsigned short);
static void nsp32_msgin_occur(nsp32_hw_data *, unsigned long, unsigned short);
static void nsp32_analyze_sdtr(nsp32_hw_data *);
static int nsp32_search_period_entry(nsp32_hw_data *,struct nsp32_target *, unsigned char);
static void nsp32_set_async(nsp32_hw_data *, struct nsp32_target *);
static void nsp32_set_max_sync(nsp32_hw_data *, struct nsp32_target *, unsigned char *, unsigned char *);
static void nsp32_set_sync_entry(nsp32_hw_data *, struct nsp32_target *, int, unsigned char);
static void nsp32_wait_req(nsp32_hw_data *, int);
static void nsp32_wait_sack(nsp32_hw_data *, int);
static void nsp32_sack_assert(nsp32_hw_data *);
static void nsp32_sack_negate(nsp32_hw_data *);
static void nsp32_do_bus_reset(nsp32_hw_data *);
static void do_nsp32_isr(int, void *, struct pt_regs *);
static int nsp32_getprom_param(nsp32_hw_data *);
static int nsp32_getprom_new(nsp32_hw_data *);
static int nsp32_getprom_standard(nsp32_hw_data *);
static int nsp32_prom_read(nsp32_hw_data *, int);
static void nsp32_prom_start(nsp32_hw_data *);
static void nsp32_prom_stop(nsp32_hw_data *);
static void nsp32_prom_write(nsp32_hw_data *, int);
static int nsp32_prom_fetch(nsp32_hw_data *);
static inline void nsp32_prom_set(nsp32_hw_data *, int, int);
static inline int nsp32_prom_get(nsp32_hw_data *, int);
/*
* max_sectors is currently limited up to 128.
*/
static Scsi_Host_Template driver_template = {
.proc_name = "nsp32",
.name = "Workbit NinjaSCSI-32Bi/UDE",
.proc_info = nsp32_proc_info,
.detect = nsp32_detect,
.info = nsp32_info,
.queuecommand = nsp32_queuecommand,
.can_queue = 1,
.sg_tablesize = NSP_SG_SIZE,
.max_sectors = 128,
.cmd_per_lun = 1,
.this_id = 7,
.use_clustering = DISABLE_CLUSTERING,
.eh_abort_handler = nsp32_eh_abort,
.eh_device_reset_handler = NULL,
.eh_bus_reset_handler = nsp32_eh_bus_reset,
.eh_host_reset_handler = nsp32_eh_host_reset,
.reset = nsp32_reset,
.release = nsp32_release,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2))
.use_new_eh_code = 1,
#else
/* .highmem_io = 1, */
#endif
};
#include "nsp32_io.h"
/*
* debug, error print
*/
#define nsp32_msg(type, args...) \
nsp32_message(__FUNCTION__, __LINE__, (type), ## args)
#define nsp32_dbg(mask, args...) \
nsp32_dmessage(__FUNCTION__, __LINE__, (mask), ## args)
#ifndef NSP32_DEBUG
# define NSP32_DEBUG_MASK 0x000000
#else
# define NSP32_DEBUG_MASK 0xffffff
#endif
#define NSP32_DEBUG_QUEUECOMMAND 0x000001
#define NSP32_DEBUG_REGISTER 0x000002
#define NSP32_DEBUG_AUTOSCSI 0x000004
#define NSP32_DEBUG_INTR 0x000008
#define NSP32_DEBUG_SGLIST 0x000010
#define NSP32_DEBUG_BUSFREE 0x000020
#define NSP32_DEBUG_CDB_CONTENTS 0x000040
#define NSP32_DEBUG_RESELECTION 0x000080
#define NSP32_DEBUG_MSGINOCCUR 0x000100
#define NSP32_DEBUG_EEPROM 0x000200
#define NSP32_DEBUG_MSGOUTOCCUR 0x000400
#define NSP32_DEBUG_BUSRESET 0x000800
#define NSP32_DEBUG_RESTART 0x001000
#define NSP32_DEBUG_SYNC 0x002000
#define NSP32_DEBUG_WAIT 0x004000
#define NSP32_DEBUG_TARGETFLAG 0x008000
#define NSP32_DEBUG_PROC 0x010000
#define NSP32_DEBUG_INIT 0x020000
#define NSP32_SPECIAL_PRINT_REGISTER 0x100000
#define NSP32_DEBUG_BUF_LEN 100
static void nsp32_message(char *func, int line, char *type, char *fmt, ...)
{
va_list args;
char buf[NSP32_DEBUG_BUF_LEN];
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
#ifndef NSP32_DEBUG
printk("%snsp32: %s\n", type, buf);
#else
printk("%snsp32: %s (%d): %s\n", type, func, line, buf);
#endif
}
static void nsp32_dmessage(char *func, int line, int mask, char *fmt, ...)
{
va_list args;
char buf[NSP32_DEBUG_BUF_LEN];
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
if (mask & NSP32_DEBUG_MASK) {
printk("Ninja: %d %s (%d): %s\n", mask, func, line, buf);
}
}
#ifdef NSP32_DEBUG
# include "nsp32_debug.c"
#else
# define show_command(arg) /* */
# define show_busphase(arg) /* */
# define show_autophase(arg) /* */
#endif
#ifdef NSP32_DEBUG
static int pc_debug = NSP32_DEBUG;
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(/*KERN_DEBUG*/ args)
#else
#define DEBUG(n, args...)
#endif
/*
* IDENTIFY Message
*/
static void nsp32_build_identify(nsp32_hw_data *data, Scsi_Cmnd *SCpnt)
{
int pos = data->msgoutlen;
data->msgoutbuf[pos++] =
0x80 | /* Identify */
#if 0
/* XXX: Auto DiscPriv detection is progressing... */
0x40 | /* DiscPriv */
#endif
SCpnt->lun; /* LUNTRN */
data->msgoutlen = pos;
}
/*
* SDTR Message Routine
*/
static void nsp32_build_sdtr(nsp32_hw_data *data,
unsigned char period, unsigned char offset)
{
int pos = data->msgoutlen;
data->msgoutbuf[pos++] = EXTENDED_MESSAGE;
data->msgoutbuf[pos++] = EXTENDED_SDTR_LEN;
data->msgoutbuf[pos++] = EXTENDED_SDTR;
data->msgoutbuf[pos++] = period;
data->msgoutbuf[pos++] = offset;
data->msgoutlen = pos;
}
/*
* No Operation Message
*/
static void nsp32_build_nop(nsp32_hw_data *data)
{
int pos = data->msgoutlen;
if (pos != 0) {
nsp32_msg(KERN_WARNING,
"Some messages are already contained!");
return;
}
data->msgoutbuf[pos++] = NOP;
data->msgoutlen = pos;
}
/*
* Reject Message
*/
static void nsp32_build_reject(nsp32_hw_data *data)
{
int pos = data->msgoutlen;
data->msgoutbuf[pos++] = MESSAGE_REJECT;
data->msgoutlen = pos;
}
/*
* timer
*/
#if 0
static void nsp32_start_timer(Scsi_Cmnd *SCpnt, int time)
{
unsigned int base = SCpnt->host->io_port;
DEBUG(0, __func__ " time=%d\n", time);
if (time & (~TIMER_CNT_MASK)) {
printk("timer set overflow\n");
}
nsp32_write2(base, TIMER_SET, time & TIMER_CNT_MASK);
}
#endif
/*
* set SCSI command and other parameter to asic, and start selection phase
*/
static int nsp32hw_start_selection(Scsi_Cmnd *SCpnt, nsp32_hw_data *data)
{
unsigned int host_id = SCpnt->host->this_id;
unsigned int base = SCpnt->host->io_port;
unsigned char target = SCpnt->target;
unsigned char *param = data->autoparam;
unsigned char phase, arbit;
int i, time;
unsigned int msgout;
unsigned long l;
unsigned short s;
/*
* check bus free
*/
phase = nsp32_read1(base, SCSI_BUS_MONITOR);
if (phase != BUSMON_BUS_FREE) {
nsp32_msg(KERN_WARNING, "bus busy");
show_busphase(phase & BUSMON_PHASE_MASK);
SCpnt->result = DID_BUS_BUSY << 16;
return FALSE;
}
/*
* message out
*
* Note: If the range of msgoutlen is 1 - 3, fill scsi_msgout.
* over 3 messages needs another routine.
*/
if (data->msgoutlen == 0) {
nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");
SCpnt->result = DID_ERROR << 16;
return FALSE;
} else if (data->msgoutlen > 0 && data->msgoutlen <= 3) {
msgout = 0;
for (i=0; i<data->msgoutlen; i++) {
/*
* the sending order of the message is:
* MCNT 3: MSG#0 -> MSG#1 -> MSG#2
* MCNT 2: MSG#1 -> MSG#2
* MCNT 1: MSG#2
*/
msgout >>= 8;
msgout |= (unsigned int)(data->msgoutbuf[i] << 24);
}
msgout |= MV_VALID; /* MV valid */
msgout |= (unsigned int)data->msgoutlen; /* len */
} else {
/* data->msgoutlen > 3 */
msgout = 0;
}
/*
* setup asic parameter
*/
memset(param, 0, AUTOPARAM_SIZE);
/* cdb */
for (i=0; i<SCpnt->cmd_len; i++) {
param[4*i] = SCpnt->cmnd[i];
}
/* message out */
param[4*0x10 +0] = (msgout & 0x000000ff) >> 0;
param[4*0x10 +1] = (msgout & 0x0000ff00) >> 8;
param[4*0x10 +2] = (msgout & 0x00ff0000) >> 16;
param[4*0x10 +3] = (msgout & 0xff000000) >> 24;
/* syncreg, ackwidth, target id, sampleing rate */
param[4*0x11 +0] = data->curtarget->syncreg;
param[4*0x11 +1] = data->curtarget->ackwidth;
param[4*0x11 +2] = BIT(host_id) | BIT(target);
param[4*0x11 +3] = 0;
/* command control */
s = (CLEAR_CDB_FIFO_POINTER | AUTOSCSI_START |
AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02 | AUTO_ATN);
param[4*0x12 +0] = (s & 0x00ff) >> 0;
param[4*0x12 +1] = (s & 0xff00) >> 8;
/* transfer control */
s = 0;
switch (data->trans_method) {
case NSP32_TRANSFER_BUSMASTER:
s |= BM_START;
break;
case NSP32_TRANSFER_MMIO:
s |= CB_MMIO_MODE;
break;
case NSP32_TRANSFER_PIO:
s |= CB_IO_MODE;
break;
default:
nsp32_msg(KERN_ERR, "unknown trans_method");
}
/*
* ORed BLIEND_MODE, FIFO intr is decreased, instead of PCI bus waits.
* For bus master transfer, it's taken off.
*/
s |= (TRANSFER_GO | ALL_COUNTER_CLR);
param[4*0x12 +2] = (s & 0x00ff) >> 0;
param[4*0x12 +3] = (s & 0xff00) >> 8;
/* sg table addr */
l = data->curlunt->sglun_paddr;
param[4*0x13 +0] = (l & 0x000000ff) >> 0;
param[4*0x13 +1] = (l & 0x0000ff00) >> 8;
param[4*0x13 +2] = (l & 0x00ff0000) >> 16;
param[4*0x13 +3] = (l & 0xff000000) >> 24;
/*
* transfer parameter to asic
*/
nsp32_write4(base, SGT_ADR, virt_to_bus(param));
nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER |
AUTO_PARAMETER );
/*
* Arbitration Status Check
*
* Note: Arbitration counter is wait during ARBIT_GO is not lifting.
* Using udelay(1) consumes CPU time and system time, but
* arbitration delay time is defined minimal 2.4us in SCSI
* specification, thus udelay works as coarse grained wait timer.
*/
time = 0;
do {
arbit = nsp32_read1(base, ARBIT_STATUS);
nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit=0x%x", arbit);
} while ((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 &&
(time++ <= 1000));
nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
"arbit: 0x%x, delay time: %d", arbit, time);
if (arbit & ARBIT_WIN) {
SCpnt->result = DID_OK << 16;
/* PCI LED on! */
nsp32_index_write1(base, EXT_PORT, LED_ON);
} else if (arbit & ARBIT_FAIL) {
SCpnt->result = DID_BUS_BUSY << 16;
nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
return FALSE;
} else {
/* unknown error or ARBIT_GO timeout */
nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit fail");
SCpnt->result = DID_NO_CONNECT << 16;
nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
return FALSE;
}
/*
* clear Arbit
*/
nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
return TRUE;
}
/*
* Selection with AUTO SCSI (without AUTO PARAMETER)
*/
static int nsp32_selection_autoscsi(Scsi_Cmnd *SCpnt, nsp32_hw_data *data)
{
unsigned char phase;
unsigned char arbit;
int status;
int i;
unsigned short command = 0;
int time = 0;
unsigned int msgout = 0;
unsigned short execph;
unsigned int base = data->BaseAddress;
/*
* IRQ disable
*/
nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
/*
* check bus line
*/
phase = nsp32_read1(base, SCSI_BUS_MONITOR);
if(((phase & BUSMON_BSY) == 1) ||
(phase & BUSMON_SEL) == 1) {
nsp32_msg(KERN_WARNING, "bus busy");
SCpnt->result = DID_BUS_BUSY << 16;
status = 1;
goto out;
}
/*
* clear execph
*/
execph = nsp32_read2(base, SCSI_EXECUTE_PHASE);
/*
* clear FIFO counter to set CDBs
*/
nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER);
/*
* set CDB0 - CDB15
*/
for (i=0; i<SCpnt->cmd_len; i++) {
nsp32_write1(base, COMMAND_DATA, SCpnt->cmnd[i]);
}
nsp32_dbg(NSP32_DEBUG_CDB_CONTENTS, "CDB[0]=[0x%x]", SCpnt->cmnd[i]);
/*
* set SCSIOUT LATCH(initiator)/TARGET(target) (ORed) ID
*/
nsp32_write1(base, SCSI_OUT_LATCH_TARGET_ID,
((1 << NSP32_HOST_SCSIID) | (1 << SCpnt->target)));
/*
* set SCSI MSGOUT REG
*
* Note: If the range of msgoutlen is 1 - 3, fill scsi_msgout.
* over 3 messages needs another routine.
*/
if (data->msgoutlen == 0) {
nsp32_msg(KERN_ERR,
"SCSI MsgOut without any message!");
SCpnt->result = DID_ERROR << 16;
status = 1;
goto out;
} else if (data->msgoutlen > 0 && data->msgoutlen <= 3) {
msgout = 0;
for (i=0; i<data->msgoutlen; i++) {
/*
* the sending order of the message is:
* MCNT 3: MSG#0 -> MSG#1 -> MSG#2
* MCNT 2: MSG#1 -> MSG#2
* MCNT 1: MSG#2
*/
msgout >>= 8;
msgout |= (unsigned int)(data->msgoutbuf[i] << 24);
}
msgout |= MV_VALID; /* MV valid */
msgout |= (unsigned int)data->msgoutlen; /* len */
nsp32_write4(base, SCSI_MSG_OUT, msgout);
} else {
/* data->msgoutlen > 3 */
nsp32_write4(base, SCSI_MSG_OUT, 0);
}
/*
* set selection timeout(= 250ms)
*/
nsp32_write2(base, SEL_TIME_OUT, SEL_TIMEOUT_TIME);
/*
* set smpl rate
*
* TODO: smpl_rate (BASE+0F) is 0 when internal clock = 40MHz.
* check other internal clock!
*/
nsp32_write1(base, SREQ_SMPL_RATE, 0);
/*
* clear Arbit
*/
nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
/*
* set SYNCREG
* Don't set BM_START_ADR before setting this register.
*/
nsp32_write1(base, SYNC_REG, data->curtarget->syncreg);
/*
* set ACKWIDTH
*/
nsp32_write1(base, ACK_WIDTH, data->curtarget->ackwidth);
nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
"syncreg=0x%x, ackwidth=0x%x, sgtpaddr=0x%x, id=0x%x",
nsp32_read1(base, SYNC_REG), nsp32_read1(base, ACK_WIDTH),
nsp32_read4(base, SGT_ADR), nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "msgoutlen=%d, msgout=0x%x",
data->msgoutlen, msgout);
/*
* set SGT ADDR (physical address)
*/
nsp32_write4(base, SGT_ADR, data->curlunt->sglun_paddr);
/*
* set TRANSFER CONTROL REG
*/
command = 0;
command |= ( TRANSFER_GO | ALL_COUNTER_CLR);
if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
if (SCpnt->request_bufflen > 0) {
command |= BM_START;
}
} else if (data->trans_method & NSP32_TRANSFER_MMIO) {
command |= CB_MMIO_MODE;
} else if (data->trans_method & NSP32_TRANSFER_PIO) {
command |= CB_IO_MODE;
}
nsp32_write2(base, TRANSFER_CONTROL, command);
/*
* start AUTO SCSI, kick off arbitration
*/
command = 0;
command |= (CLEAR_CDB_FIFO_POINTER
| AUTOSCSI_START
| AUTO_MSGIN_00_OR_04
| AUTO_MSGIN_02
| AUTO_ATN);
nsp32_write2(base, COMMAND_CONTROL, command);
/*
* Arbitration Status Check
*
* Note: Arbitration counter is wait during ARBIT_GO is not lifting.
* Using udelay(1) consumes CPU time and system time, but
* arbitration delay time is defined minimal 2.4us in SCSI
* specification, thus udelay works as coarse grained wait timer.
*/
time = 0;
while(1) {
arbit = nsp32_read1(base, ARBIT_STATUS);
if(arbit & ARBIT_GO) {
udelay(1);
time++;
if ( time > ARBIT_TIMEOUT_TIME ) {
/* something lock up! guess no connection */
SCpnt->result = DID_NO_CONNECT << 16;
status = FALSE;
goto out;
}
} else {
break;
}
};
nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit: 0x%x, delay time: %d", arbit, time);
/*
* check Arbitration Status Result
*/
if(arbit & ARBIT_WIN) {
/* Arbitration succeeded */
status = TRUE;
SCpnt->result = DID_OK << 16;
/* PCI LED on! */
nsp32_index_write1(base, EXT_PORT, LED_ON);
} else if(arbit & ARBIT_FAIL) {
/* Arbitration failed */
status = FALSE;
SCpnt->result = DID_BUS_BUSY << 16;
} else {
/* unknown error? */
status = FALSE;
SCpnt->result = DID_ERROR << 16;
SCpnt->result = DID_NO_CONNECT << 16;
}
/*
* clear Arbit
*/
nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
out:
/*
* IRQ enable
*/
nsp32_write2(base, IRQ_CONTROL, 0);
return(status);
}
/*
* reselection
*
* Note: This reselection routine is called from msgin_occur,
* reselection target id&lun must be already set.
* SCSI-2 says IDENTIFY implies RESTORE_POINTER operation.
*/
static int nsp32_reselection(nsp32_hw_data *data, unsigned char newlun)
{
unsigned int base = data->BaseAddress;
unsigned char tmpid, newid;
nsp32_dbg(NSP32_DEBUG_RESELECTION, "enter");
/*
* calculate reselected SCSI ID
*/
tmpid = nsp32_read1(base, RESELECT_ID);
tmpid &= 0x7f;
newid = 0;
while (tmpid) {
if (tmpid & 1) {
break;
}
tmpid >>= 1;
newid++;
}
/*
* If reselected New ID:LUN is not existed
* or current nexus is not existed, unexpected
* reselection is occured. Send reject message.
*/
if (newid >= MAX_TARGET || newlun >= MAX_LUN) {
nsp32_msg(KERN_WARNING, "unknown id/lun");
return FALSE;
} else if(data->lunt[newid][newlun]->SCpnt == NULL) {
nsp32_msg(KERN_WARNING, "no SCSI command is processing");
return FALSE;
}
data->pid = newid;
data->plun = newlun;
data->curtarget = &data->target[newid];
data->curlunt = data->lunt[newid][newlun];
/* reset SACK/SavedACK counter (or ALL clear?) */
nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
return TRUE;
}
/*
* nsp32hw_setup_sg_table - build scatter gather list for transfer data
* with bus master.
*
* Note: NinjaSCSI-32Bi/UDE bus master can not transfer over 64KB at a time.
*/
static int nsp32hw_setup_sg_table(Scsi_Cmnd *SCpnt, nsp32_hw_data *data)
{
struct scatterlist *sgl;
struct nsp32_sgtable *sgt = data->curlunt->sglun->sgt;
int num, i;
if (SCpnt->request_bufflen == 0) {
return TRUE;
}
if (sgt == NULL) {
nsp32_dbg(NSP32_DEBUG_SGLIST, "SGT == null");
return FALSE;
}
if (SCpnt->use_sg) {
sgl = (struct scatterlist *)SCpnt->request_buffer;
num = pci_map_sg(data->Pci, sgl, SCpnt->use_sg,
scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
for (i=0; i<num; i++) {
/*
* Build nsp32_sglist, substitute sg dma addresses.
*/
sgt[i].addr = cpu_to_le32(sg_dma_address(sgl));
sgt[i].len = cpu_to_le32(sg_dma_len(sgl));
sgl++;
if (sgt[i].len > 65536) {
nsp32_msg(KERN_ERR,
"can't transfer over 64KB at a time");
return FALSE;
}
nsp32_dbg(NSP32_DEBUG_SGLIST,
"num 0x%x : addr 0x%lx len 0x%x",
i, sgt[i].addr, sgt[i].len);
}
sgt[num-1].len |= NSP32_SG_END_SGT; /* set end mark */
} else {
SCpnt->SCp.have_data_in = pci_map_single(data->Pci,
SCpnt->request_buffer, SCpnt->request_bufflen,
scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
sgt[0].addr = cpu_to_le32(SCpnt->SCp.have_data_in);
sgt[0].len = cpu_to_le32(SCpnt->request_bufflen);
sgt[0].len |= NSP32_SG_END_SGT; /* set end mark */
nsp32_dbg(NSP32_DEBUG_SGLIST, "single : addr 0x%lx len=0x%x",
sgt[0].addr, sgt[0].len);
}
return TRUE;
}
static int nsp32_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->host->hostdata;
struct nsp32_target *target;
struct nsp32_lunt *curlunt;
int ret;
nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
"enter. target: 0x%x LUN: 0x%x cmnd: 0x%x cmndlen: 0x%x "
"use_sg: 0x%x reqbuf: 0x%lx reqlen: 0x%x",
SCpnt->target, SCpnt->lun, SCpnt->cmnd[0], SCpnt->cmd_len,
SCpnt->use_sg, SCpnt->request_buffer, SCpnt->request_bufflen);
if (data->CurrentSC != NULL ) {
nsp32_msg(KERN_ERR, "Currentsc != NULL. Cancel this command request");
data->CurrentSC = NULL;
SCpnt->result = DID_NO_CONNECT << 16;
done(SCpnt);
return 1;
}
/* check target ID is not same as this initiator ID */
if (SCpnt->target == NSP32_HOST_SCSIID) {
SCpnt->result = DID_BAD_TARGET << 16;
done(SCpnt);
return 1;
}
/* check target LUN is allowable value */
if (SCpnt->lun >= MAX_LUN) {
SCpnt->result = DID_BAD_TARGET << 16;
done(SCpnt);
return 1;
}
show_command(SCpnt);
SCpnt->scsi_done = done;
data->CurrentSC = SCpnt;
SCpnt->SCp.Status = CHECK_CONDITION;
SCpnt->SCp.Message = 0;
SCpnt->resid = 0; //SCpnt->request_bufflen;
SCpnt->SCp.ptr = (char *) SCpnt->request_buffer;
SCpnt->SCp.this_residual = SCpnt->request_bufflen;
SCpnt->SCp.buffer = NULL;
SCpnt->SCp.buffers_residual = 0;
/* initialize data */
data->msgoutlen = 0;
data->msginlen = 0;
curlunt = data->lunt[SCpnt->target][SCpnt->lun];
curlunt->SCpnt = SCpnt;
curlunt->save_datp = 0;
curlunt->msgin03 = FALSE;
data->curlunt = curlunt;
data->pid = SCpnt->target;
data->plun = SCpnt->lun;
ret = nsp32hw_setup_sg_table(SCpnt, data);
if (ret == FALSE) {
SCpnt->result = DID_ERROR << 16;
nsp32_scsi_done(data, SCpnt);
}
/* Build IDENTIFY */
nsp32_build_identify(data, SCpnt);
/*
* If target is the first time to transfer after the reset
* (target don't have SDTR_DONE and SDTR_INITIATOR), sync
* message SDTR is needed to do synchronous transfer.
*/
target = &data->target[SCpnt->target];
data->curtarget = target;
if (!(target->sync_flag & (SDTR_DONE | SDTR_INITIATOR | SDTR_TARGET))) {
unsigned char period, offset;
if (trans_mode != ASYNC_MODE) {
nsp32_set_max_sync(data, target, &period, &offset);
nsp32_build_sdtr(data, period, offset);
target->sync_flag |= SDTR_INITIATOR;
} else {
nsp32_set_async(data, target);
target->sync_flag |= SDTR_DONE;
}
nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
"SDTR: entry: %d start_period: 0x%x offset: 0x%x\n",
target->limit_entry, period, offset);
} else if (target->sync_flag & SDTR_INITIATOR) {
/*
* It was negotiating SDTR with target, sending from the
* initiator, but there are no chance to remove this flag.
* Set async because we don't get proper negotiation.
*/
nsp32_set_async(data, target);
target->sync_flag &= ~SDTR_INITIATOR;
target->sync_flag |= SDTR_DONE;
nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
"SDTR_INITIATOR: fall back to async");
} else if (target->sync_flag & SDTR_TARGET) {
/*
* It was negotiating SDTR with target, sending from target,
* but there are no chance to remove this flag. Set async
* because we don't get proper negotiation.
*/
nsp32_set_async(data, target);
target->sync_flag &= ~SDTR_TARGET;
target->sync_flag |= SDTR_DONE;
nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
"Unknown SDTR from target is reached, fall back to async.");
}
nsp32_dbg(NSP32_DEBUG_TARGETFLAG,
"target: %d sync_flag: 0x%x syncreg: 0x%x ackwidth: 0x%x",
SCpnt->target, target->sync_flag, target->syncreg,
target->ackwidth);
/* Selection */
if (auto_param == 0) {
ret = nsp32hw_start_selection(SCpnt, data);
} else {
ret = nsp32_selection_autoscsi(SCpnt, data);
}
if (ret != TRUE) {
nsp32_scsi_done(data, SCpnt);
return 1;
}
return 0;
}
/* initialize asic */
static int nsp32hw_init(struct Scsi_Host *host)
{
unsigned int base = host->io_port;
unsigned short irq_stat;
unsigned long lc_reg;
unsigned char power;
nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
lc_reg = nsp32_index_read4(base, CFG_LATE_CACHE);
if ((lc_reg & 0xff00) == 0) {
lc_reg |= (0x20 << 8);
nsp32_index_write2(base, CFG_LATE_CACHE, lc_reg & 0xffff);
}
nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
nsp32_write2(base, TRANSFER_CONTROL, 0);
nsp32_write4(base, BM_CNT, 0);
nsp32_write2(base, SCSI_EXECUTE_PHASE, 0);
do {
irq_stat = nsp32_read2(base, IRQ_STATUS);
} while (irq_stat & IRQSTATUS_ANY_IRQ);
nsp32_dbg(NSP32_DEBUG_INIT, "irq_stat 0x%x", irq_stat);
/*
* Fill FIFO_FULL_SHLD, FIFO_EMPTY_SHLD. Below parameter is
* designated by specification.
*/
if ((data->trans_method & NSP32_TRANSFER_PIO) ||
(data->trans_method & NSP32_TRANSFER_MMIO)) {
nsp32_index_write1(base, FIFO_FULL_SHLD_COUNT, 0x40);
} else if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
nsp32_index_write1(base, FIFO_FULL_SHLD_COUNT, 0x10);
}
nsp32_index_write1(base, FIFO_EMPTY_SHLD_COUNT, 0x60);
nsp32_dbg(NSP32_DEBUG_INIT, "full 0x%x emp 0x%x",
nsp32_index_read1(base, FIFO_FULL_SHLD_COUNT),
nsp32_index_read1(base, FIFO_EMPTY_SHLD_COUNT));
nsp32_index_write1(base, CLOCK_DIV, data->clock);
nsp32_index_write1(base, BM_CYCLE, MEMRD_CMD1 | SGT_AUTO_PARA_MEMED_CMD);
nsp32_write1(base, PARITY_CONTROL, 0); /* parity check is disable */
/*
* initialize I_MISC_WRRD register
*
* Note: Designated parameters is obeyed as following:
* MISC_SCSI_DIRECTION_DETECTOR_SELECT: It must be set.
* MISC_MASTER_TERMINATION_SELECT: It must be set.
* MISC_BMREQ_NEGATE_TIMING_SEL: It should be set.
* MISC_AUTOSEL_TIMING_SEL: It should be set.
* MISC_BMSTOP_CHANGE2_NONDATA_PHASE: It should be set.
* MISC_DELAYED_BMSTART: It's selected for safety.
*
* Note: If MISC_BMSTOP_CHANGE2_NONDATA_PHASE is set, then
* we have to set TRANSFERCONTROL_BM_START as 0 and set
* appropriate value before restarting bus master transfer.
*/
nsp32_index_write2(base, MISC_WR,
(SCSI_DIRECTION_DETECTOR_SELECT |
DELAYED_BMSTART |
MASTER_TERMINATION_SELECT |
BMREQ_NEGATE_TIMING_SEL |
AUTOSEL_TIMING_SEL |
BMSTOP_CHANGE2_NONDATA_PHASE));
nsp32_index_write1(base, TERM_PWR_CONTROL, 0);
power = nsp32_index_read1(base, TERM_PWR_CONTROL);
if (!(power & SENSE)) {
nsp32_msg(KERN_INFO, "term power on");
nsp32_index_write1(base, TERM_PWR_CONTROL, BPWR);
}
nsp32_write2(base, TIMER_SET, TIMER_STOP);
nsp32_write2(base, TIMER_SET, TIMER_STOP);
nsp32_write1(base, SYNC_REG, 0);
nsp32_write1(base, ACK_WIDTH, 0);
nsp32_write2(base, SEL_TIME_OUT, 10000); /* 25us x10000 = 250ms defined in SCSI */
/*
* enable to select designated IRQ (except for
* IRQSELECT_SERR, IRQSELECT_PERR, IRQSELECT_BMCNTERR)
*/
nsp32_index_write2(base, IRQ_SELECT, IRQSELECT_TIMER_IRQ |
IRQSELECT_SCSIRESET_IRQ |
IRQSELECT_FIFO_SHLD_IRQ |
IRQSELECT_RESELECT_IRQ |
IRQSELECT_PHASE_CHANGE_IRQ |
IRQSELECT_AUTO_SCSI_SEQ_IRQ |
//IRQSELECT_BMCNTERR_IRQ |
IRQSELECT_TARGET_ABORT_IRQ |
IRQSELECT_MASTER_ABORT_IRQ );
nsp32_write2(base, IRQ_CONTROL, 0);
/* PCI LED off */
nsp32_index_write1(base, EXT_PORT_DDR, LED_OFF);
nsp32_index_write1(base, EXT_PORT, LED_OFF);
return TRUE;
}
/* interrupt routine */
static void do_nsp32_isr(int irq, void *dev_id, struct pt_regs *regs)
{
nsp32_hw_data *data = dev_id;
unsigned int base = data->BaseAddress;
Scsi_Cmnd *SCpnt = data->CurrentSC;
unsigned short auto_stat, irq_stat, trans_stat;
unsigned char busmon, busphase;
unsigned long flags;
int ret;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
struct Scsi_Host *host = data->Host;
spin_lock_irqsave(host->host_lock, flags);
#else
spin_lock_irqsave(&io_request_lock, flags);
#endif
/*
* IRQ check, then enable IRQ mask
*/
irq_stat = nsp32_read2(base, IRQ_STATUS);
nsp32_dbg(NSP32_DEBUG_INTR,
"enter IRQ: %d, IRQstatus: 0x%x", irq, irq_stat);
/* is this interrupt comes from Ninja asic? */
if ((irq_stat & IRQSTATUS_ANY_IRQ) == 0) {
nsp32_msg(KERN_INFO, "spurious interrupt: irq other 0x%x", irq_stat);
goto out2;
}
nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
busmon = nsp32_read1(base, SCSI_BUS_MONITOR);
busphase = busmon & BUSMON_PHASE_MASK;
trans_stat = nsp32_read2(base, TRANSFER_STATUS);
if ((irq_stat == 0xffff) && (trans_stat == 0xffff)) {
nsp32_msg(KERN_INFO, "card disconnect");
if (data->CurrentSC != NULL) {
nsp32_msg(KERN_INFO, "clean up current SCSI command");
SCpnt->result = DID_BAD_TARGET << 16;
nsp32_scsi_done(data, SCpnt);
}
goto out;
}
/* Timer IRQ */
if (irq_stat & IRQSTATUS_TIMER_IRQ) {
DEBUG(0, "timer stop\n");
nsp32_write2(base, TIMER_SET, TIMER_STOP);
goto out;
}
/* SCSI reset */
if (irq_stat & IRQSTATUS_SCSIRESET_IRQ) {
nsp32_msg(KERN_INFO, "detected someone do bus reset");
nsp32_do_bus_reset(data);
if (SCpnt != NULL) {
SCpnt->result = DID_RESET << 16;
nsp32_scsi_done(data, SCpnt);
}
goto out;
}
if (SCpnt == NULL) {
nsp32_msg(KERN_WARNING, "SCpnt==NULL this can't be happen\n");
nsp32_msg(KERN_WARNING, "irq_stat=0x%x trans_stat=0x%x\n", irq_stat, trans_stat);
goto out;
}
/*
* AutoSCSI Interrupt.
* Note: This interrupt is occured when AutoSCSI is finished. Then
* check SCSIEXECUTEPHASE, and do appropriate action. Each phases are
* recorded when AutoSCSI sequencer has been processed.
*/
if(irq_stat & IRQSTATUS_AUTOSCSI_IRQ) {
/* getting SCSI executed phase */
auto_stat = nsp32_read2(base, SCSI_EXECUTE_PHASE);
nsp32_write2(base, SCSI_EXECUTE_PHASE, 0);
/* Selection Timeout, go busfree phase. */
if (auto_stat & SELECTION_TIMEOUT) {
nsp32_dbg(NSP32_DEBUG_INTR,
"selection timeout occured");
SCpnt->result = DID_TIME_OUT << 16;
nsp32_scsi_done(data, SCpnt);
goto out;
}
if (auto_stat & MSGOUT_PHASE) {
/*
* MsgOut phase was processed.
* If MSG_IN_OCCUER is not set, then MsgOut phase is
* completed. Thus, msgoutlen must reset. Otherwise,
* nothing to do here. If MSG_OUT_OCCUER is occured,
* then we will encounter the condition and check.
*/
if (!(auto_stat & MSG_IN_OCCUER) &&
(data->msgoutlen <= 3)) {
/*
* !MSG_IN_OCCUER && msgoutlen <=3
* ---> AutoSCSI with MSGOUTreg is processed.
*/
data->msgoutlen = 0;
};
nsp32_dbg(NSP32_DEBUG_INTR, "MsgOut phase processed");
}
if ((auto_stat & DATA_IN_PHASE) &&
(SCpnt->resid > 0) &&
((nsp32_read2(base, FIFO_REST_CNT) & FIFO_REST_MASK) != 0)) {
printk( "auto+fifo\n");
//nsp32_pio_read(SCpnt);
}
if (auto_stat & (DATA_IN_PHASE | DATA_OUT_PHASE)) {
/* DATA_IN_PHASE/DATA_OUT_PHASE was processed. */
nsp32_dbg(NSP32_DEBUG_INTR,
"Data in/out phase processed");
/* read BMCNT, SGT pointer addr */
nsp32_dbg(NSP32_DEBUG_INTR, "BMCNT=0x%lx",
nsp32_read4(base, BM_CNT));
nsp32_dbg(NSP32_DEBUG_INTR, "addr=0x%lx",
nsp32_read4(base, SGT_ADR));
nsp32_dbg(NSP32_DEBUG_INTR, "SACK=0x%lx",
nsp32_read4(base, SACK_CNT));
nsp32_dbg(NSP32_DEBUG_INTR, "SSACK=0x%lx",
nsp32_read4(base, SAVED_SACK_CNT));
}
/*
* MsgIn Occur
*/
if (auto_stat & MSG_IN_OCCUER) {
nsp32_msgin_occur(data, irq_stat, auto_stat);
}
/*
* MsgOut Occur
*/
if (auto_stat & MSG_OUT_OCCUER) {
nsp32_msgout_occur(data);
}
/*
* Bus Free Occur
*/
if (auto_stat & BUS_FREE_OCCUER) {
ret = nsp32_busfree_occur(data, auto_stat);
if (ret == TRUE) {
goto out;
}
}
if (auto_stat & STATUS_PHASE) {
/*
* Read CSB and substitute CSB for SCpnt->result
* to save status phase stutas byte.
* scsi error handler checks host_byte (DID_*:
* low level driver to indicate status), then checks
* status_byte (SCSI status byte).
*/
SCpnt->result = (int)nsp32_read1(base, SCSI_CSB_IN);
}
if (auto_stat & ILLEGAL_PHASE) {
/* Illegal phase is detected. SACK is not back. */
nsp32_msg(KERN_WARNING,
"AUTO SCSI ILLEGAL PHASE OCCUR!!!!");
/* TODO: currently we don't have any action... bus reset? */
/*
* To send back SACK, assert, wait, and negate.
*/
nsp32_sack_assert(data);
nsp32_wait_req(data, NEGATE);
nsp32_sack_negate(data);
}
if (auto_stat & COMMAND_PHASE) {
/* nothing to do */
nsp32_dbg(NSP32_DEBUG_INTR, "Command phase processed");
}
if (auto_stat & AUTOSCSI_BUSY) {
/* AutoSCSI is running */
}
show_autophase(auto_stat);
}
/* FIFO_SHLD_IRQ */
if (irq_stat & IRQSTATUS_FIFO_SHLD_IRQ) {
nsp32_dbg(NSP32_DEBUG_INTR, "FIFO IRQ");
switch(busphase) {
case BUSPHASE_DATA_OUT:
printk( "write\n");
//nsp32_pio_write(SCpnt);
break;
case BUSPHASE_DATA_IN:
printk( "read\n");
//nsp32_pio_read(SCpnt);
break;
case BUSPHASE_STATUS:
//DEBUG(0, "fifo/status\n");
SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
break;
default:
printk("fifo/other phase?\n");
printk("irq_stat=0x%x trans_stat=0x%x\n", irq_stat, trans_stat);
show_busphase(busphase);
break;
}
goto out;
}
/* Phase Change IRQ */
if (irq_stat & IRQSTATUS_PHASE_CHANGE_IRQ) {
nsp32_dbg(NSP32_DEBUG_INTR, "phase change IRQ");
switch(busphase) {
case BUSPHASE_MESSAGE_IN:
nsp32_dbg(NSP32_DEBUG_INTR, "phase chg/msg in");
nsp32_msgin_occur(data, irq_stat, 0);
break;
default:
nsp32_msg(KERN_WARNING, "phase chg/other phase?");
nsp32_msg(KERN_WARNING, "irq_stat=0x%x trans_stat=0x%x\n",
irq_stat, trans_stat);
show_busphase(busphase);
break;
}
goto out;
}
/* PCI_IRQ */
if (irq_stat & IRQSTATUS_PCI_IRQ) {
nsp32_dbg(NSP32_DEBUG_INTR, "PCI IRQ occured");
/* Do nothing */
}
/* BMCNTERR_IRQ */
if (irq_stat & IRQSTATUS_BMCNTERR_IRQ) {
nsp32_msg(KERN_ERR, "Received unexpected BMCNTERR IRQ! ");
/*
* TODO: To be implemented improving bus master
* transfer reliablity when BMCNTERR is occured in
* AutoSCSI phase described in specification.
*/
}
#if 0
printk("irq_stat=0x%x trans_stat=0x%x\n", irq_stat, trans_stat);
show_busphase(busphase);
#endif
out:
/* disable IRQ mask */
nsp32_write2(base, IRQ_CONTROL, 0);
out2:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
spin_unlock_irqrestore(&io_request_lock, flags);
#else
spin_unlock_irqrestore(host->host_lock, flags);
#endif
nsp32_dbg(NSP32_DEBUG_INTR, "exit");
return;
}
#undef SPRINTF
#define SPRINTF(args...) \
do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
static int nsp32_proc_info(char *buffer,
char **start,
off_t offset,
int length,
int hostno,
int inout)
{
char *pos = buffer;
int thislength;
unsigned long flags;
nsp32_hw_data *data;
struct Scsi_Host *host = NULL;
unsigned int base;
unsigned char mode_reg;
/* Write is not supported, just return. */
if (inout == TRUE) {
return -EINVAL;
}
/* search this HBA host */
for (host=scsi_hostlist; host; host=host->next) {
if (host->host_no == hostno) {
break;
}
}
if (host == NULL) {
return -ESRCH;
}
data = (nsp32_hw_data *)host->hostdata;
base = host->io_port;
SPRINTF("NinjaSCSI-32 status\n\n");
SPRINTF("Driver version: %s\n", nsp32_release_version);
SPRINTF("SCSI host No.: %d\n", hostno);
SPRINTF("IRQ: %d\n", host->irq);
SPRINTF("IO: 0x%lx-0x%lx\n", host->io_port, host->io_port + host->n_io_port - 1);
SPRINTF("MMIO(virtual address): 0x%lx\n", host->base);
SPRINTF("sg_tablesize: %d\n", host->sg_tablesize);
SPRINTF("Chip revision: %d\n", (nsp32_read2(base, INDEX_REG) >> 8) - 0x4f);
mode_reg = nsp32_index_read1(base, CHIP_MODE);
#ifdef CONFIG_PM
//SPRINTF("Power Management: %s\n", (mode_reg & OPTF) ? "yes" : "no");
#endif
SPRINTF("OEM: %s\n", nsp32_model[mode_reg & (OEM0|OEM1)]);
spin_lock_irqsave(&(data->Lock), flags);
SPRINTF("CurrentSC: 0x%p\n\n", data->CurrentSC);
spin_unlock_irqrestore(&(data->Lock), flags);
thislength = pos - (buffer + offset);
if(thislength < 0) {
*start = 0;
return 0;
}
thislength = MIN(thislength, length);
*start = buffer + offset;
return thislength;
}
#undef SPRINTF
/*
* Note: n_io_port is defined as 0x7f because I/O register port is
* assigned as:
* 0x800-0x8ff: memory mapped I/O port
* 0x900-0xbff: (map same 0x800-0x8ff I/O port image repeatedly)
* 0xc00-0xfff: CardBus status registers
*/
static int nsp32_detect(Scsi_Host_Template *sht)
{
struct Scsi_Host *host; /* registered host structure */
int ret;
nsp32_hw_data *data;
int i, j;
nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
/*
* register this HBA as SCSI device
*/
host = scsi_register(sht, sizeof(nsp32_hw_data));
if (host == NULL) {
nsp32_msg (KERN_ERR, "failed to scsi register");
goto err;
}
/*
* set nsp32_hw_data
*/
data = (nsp32_hw_data *)host->hostdata;
memset(data, 0, sizeof(nsp32_hw_data));
data->IrqNumber = nsp32_data_base.IrqNumber;
data->BaseAddress = nsp32_data_base.BaseAddress;
data->NumAddress = nsp32_data_base.NumAddress;
data->MmioAddress = nsp32_data_base.MmioAddress;
data->Pci = nsp32_data_base.Pci;
data->pci_devid = nsp32_data_base.pci_devid;
host->irq = data->IrqNumber;
host->io_port = data->BaseAddress;
host->unique_id = data->BaseAddress;
host->n_io_port = data->NumAddress;
host->base = data->MmioAddress;
scsi_set_pci_device(host, data->Pci);
data->Host = host;
spin_lock_init(&(data->Lock));
data->curlunt = NULL;
data->curtarget = NULL;
/*
* Bus master transfer mode is supported currently.
*/
data->trans_method = NSP32_TRANSFER_BUSMASTER;
/*
* Set clock div, CLOCK_4 (HBA has external clock, and
* dividing * 100ns/4).
* Currently CLOCK_4 has only tested, not for CLOCK_2/PCICLK yet.
*/
data->clock = CLOCK_4;
/*
* Select appropriate nsp32_sync_table and set I_CLOCKDIV.
*/
switch (data->clock) {
case CLOCK_4:
/* If data->clock is CLOCK_4, then select 40M sync table. */
data->synct = nsp32_sync_table_40M;
data->syncnum = nsp32_table_40M_num;
break;
case CLOCK_2:
/* If data->clock is CLOCK_2, then select 20M sync table. */
data->synct = nsp32_sync_table_20M;
data->syncnum = nsp32_table_20M_num;
break;
case PCICLK:
/* If data->clock is PCICLK, then select pci sync table. */
data->synct = nsp32_sync_table_pci;
data->syncnum = nsp32_table_pci_num;
break;
default:
nsp32_msg(KERN_WARNING,
"Invalid clock div is selected, set CLOCK_4.");
/* Use default value CLOCK_4 */
data->clock = CLOCK_4;
data->synct = nsp32_sync_table_40M;
data->syncnum = nsp32_table_40M_num;
}
/*
* setup nsp32_lunt
*/
data->lunt_list = (struct nsp32_lunt *)
kmalloc(sizeof(struct nsp32_lunt) * MAX_TARGET * MAX_LUN,
GFP_KERNEL);
if (data->lunt_list == NULL) {
nsp32_msg(KERN_ERR, "cannot allocate LUN memory");
goto scsi_unregister;
}
nsp32_dbg(NSP32_DEBUG_REGISTER, "0x%x 0x%x",
data->lunt_list, sizeof(struct nsp32_lunt)*MAX_TARGET*MAX_LUN);
/*
* setup DMA
*/
if (pci_set_dma_mask(data->Pci, 0xffffffffUL)) {
nsp32_msg (KERN_ERR, "failed to set PCI DMA mask");
goto kfree_lunt;
}
/*
* allocate autoparam DMA resource.
*/
data->autoparam = pci_alloc_consistent(data->Pci, AUTOPARAM_SIZE, &data->apaddr);
if (data->autoparam == NULL) {
nsp32_msg(KERN_ERR, "failed to allocate DMA memory");
goto kfree_lunt;
}
/*
* allocate scatter-gather DMA resource.
*/
data->sg_list = pci_alloc_consistent(data->Pci,
(sizeof(struct nsp32_sgtable) * NSP_SG_SIZE * MAX_TARGET * MAX_LUN),
&data->sgaddr);
if (data->sg_list == NULL) {
nsp32_msg(KERN_ERR, "failed to allocate DMA memory");
goto free_autoparam;
}
for (i=0; i<MAX_TARGET; i++) {
for (j=0; j<MAX_LUN; j++) {
data->lunt[i][j] = data->lunt_list + (i * MAX_LUN + j);
}
}
for (i=0; i<MAX_TARGET; i++) {
for (j=0; j<MAX_LUN; j++) {
struct nsp32_lunt *lp = data->lunt[i][j];
lp->sglun = (struct nsp32_sglun *)
(data->sg_list + (i * MAX_LUN + j));
lp->sglun_paddr = data->sgaddr +
(long)((i * MAX_LUN + j)
* sizeof(struct nsp32_sglun));
lp->SCpnt = NULL;
lp->save_datp = 0;
lp->msgin03 = FALSE;
lp->sg_num = 0;
lp->cur_entry = 0;
}
}
/*
* setup target
*/
for (i=0; i<MAX_TARGET; i++) {
struct nsp32_target *target = &data->target[i];
target->limit_entry = 0;
target->sync_flag = 0;
nsp32_set_async(data, target);
}
/*
* EEPROM check
*/
ret = nsp32_getprom_param(data);
if (ret == FALSE) {
data->resettime = 3; /* default 3 */
}
/*
* setup HBA
*/
nsp32hw_init(host);
snprintf(data->info_str, sizeof(data->info_str),
"NinjaSCSI-32Bi/UDE: irq %d, io 0x%lx+0x%x",
host->irq, host->io_port, host->n_io_port);
sht->name = data->info_str;
/*
* SCSI bus reset
*
* Note: It's important to reset SCSI bus in initialization phase.
* NinjaSCSI-32Bi/UDE HBA EEPROM seems to exchange SDTR when system is
* coming up, so SCSI devices connected to HBA is set as
* un-asynchronous mode. It brings the merit that this HBA is
* ready to start synchronous transfer without any preparation,
* but we are difficult to control transfer speed. In addition,
* it prevents device transfer speed from effecting EEPROM start-up
* SDTR. NinjaSCSI-32Bi/UDE has the feature if EEPROM is set as Auto
* Mode, then FAST-10M is selected when SCSI devices are connected
* same or more than 4 devices. It should be avoided depending on
* this specification Thus, resetting the SCSI bus restores all
* connected SCSI devices to asynchronous mode, then this driver
* put SDTR safely later, and we can control all SCSI device
* transfer mode.
*/
nsp32_do_bus_reset(data);
ret = request_irq(host->irq, do_nsp32_isr, SA_SHIRQ, "nsp32", data);
if (ret < 0) {
nsp32_msg(KERN_ERR, "Unable to allocate IRQ for NSP32 "
"SCSI PCI controller. Interrupt: %d\n", host->irq);
goto free_sg_list;
}
/*
* PCI IO register
*/
if(!request_region(host->io_port, host->n_io_port, "nsp32")) {
nsp32_msg(KERN_ERR,
"I/O region 0x%lx+0x%lx is already used",
data->BaseAddress, data->length);
goto free_irq;
}
return 1;
free_irq:
free_irq(host->irq, data);
free_autoparam:
pci_free_consistent(data->Pci, AUTOPARAM_SIZE, data->autoparam, data->apaddr);
free_sg_list:
pci_free_consistent(data->Pci,
(sizeof(struct nsp32_sgtable) * NSP_SG_SIZE * MAX_TARGET * MAX_LUN),
data->sg_list, data->sgaddr);
kfree_lunt:
kfree(data->lunt_list);
scsi_unregister:
scsi_unregister(host);
err:
return 0;
}
static int nsp32_release(struct Scsi_Host *shpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)shpnt->hostdata;
if (data->lunt_list) {
kfree(data->lunt_list);
}
if (data->autoparam) {
pci_free_consistent(data->Pci, AUTOPARAM_SIZE,
data->autoparam, data->apaddr);
}
if (data->sg_list) {
pci_free_consistent(data->Pci,
(sizeof(struct nsp32_sgtable) * NSP_SG_SIZE * MAX_TARGET * MAX_LUN),
data->sg_list, data->sgaddr);
}
DEBUG(0, "free irq\n");
if (shpnt->irq) {
free_irq(shpnt->irq, data);
}
DEBUG(0, "free io\n");
if (shpnt->io_port && shpnt->n_io_port) {
release_region(shpnt->io_port, shpnt->n_io_port);
}
if (data->MmioAddress != 0) {
iounmap((void *)(data->MmioAddress));
}
return 0;
}
static const char *nsp32_info(struct Scsi_Host *shpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)shpnt->hostdata;
return data->info_str;
}
/*
* error handler
*/
static int nsp32_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
{
nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%p why=%d\n", SCpnt, reset_flags);
nsp32_eh_bus_reset(SCpnt);
return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
}
static int nsp32_eh_abort(Scsi_Cmnd *SCpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->host->hostdata;
unsigned int base = data->BaseAddress;
nsp32_msg(KERN_WARNING, "abort");
if (data->curlunt->SCpnt == NULL) {
return (FAILED);
}
if (data->curtarget->sync_flag & (SDTR_INITIATOR | SDTR_TARGET)) {
/* reset SDTR negotiation */
data->curtarget->sync_flag = 0;
}
nsp32_write2(base, TRANSFER_CONTROL, 0);
nsp32_write2(base, BM_CNT, 0);
return (FAILED);
}
static int nsp32_eh_bus_reset(Scsi_Cmnd *SCpnt)
{
nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->host->hostdata;
unsigned int base = data->BaseAddress;
nsp32_msg(KERN_INFO, "Bus Reset");
nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%x", SCpnt);
nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
nsp32_do_bus_reset(data);
nsp32_write2(base, IRQ_CONTROL, 0);
return SUCCESS; /* SCSI bus reset is succeeded at any time. */
}
static void nsp32_do_bus_reset(nsp32_hw_data *data)
{
unsigned int base = data->BaseAddress;
unsigned short intrdat;
int i;
/*
* stop all transfer
* clear TRANSFERCONTROL_BM_START
* clear counter
*/
nsp32_write2(base, TRANSFER_CONTROL, 0);
nsp32_write4(base, BM_CNT, 0);
nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
/*
* fall back to asynchronous transfer mode
* initialize SDTR negotiation flag
*/
for (i=0; i<MAX_TARGET; i++) {
struct nsp32_target *target = &data->target[i];
target->sync_flag = 0;
nsp32_set_async(data, target);
}
/*
* reset SCSI bus
*/
nsp32_write1(base, SCSI_BUS_CONTROL, BUSCTL_RST);
udelay(RESET_HOLD_TIME);
nsp32_write1(base, SCSI_BUS_CONTROL, 0);
for(i = 0; i < 5; i++) {
intrdat = nsp32_read2(base, IRQ_STATUS); /* dummy read */
nsp32_dbg(NSP32_DEBUG_BUSRESET, "irq:1: 0x%x", intrdat);
}
data->CurrentSC = NULL;
}
static int nsp32_eh_host_reset(Scsi_Cmnd *SCpnt)
{
struct Scsi_Host *host = SCpnt->host;
nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
unsigned int base = data->BaseAddress;
nsp32_msg(KERN_INFO, "Host Reset");
nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%x", SCpnt);
nsp32hw_init(host);
nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
nsp32_do_bus_reset(data);
nsp32_write2(base, IRQ_CONTROL, 0);
return SUCCESS; /* Host reset is succeeded at any time. */
}
/*
* PCI/Cardbus probe/remove routine
*/
static int __devinit nsp32_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int ret;
nsp32_hw_data *data = &nsp32_data_base;
nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
ret = pci_enable_device(pdev);
if (ret) {
nsp32_msg(KERN_ERR, "failed to enable pci device");
return ret;
}
data->Pci = pdev;
data->pci_devid = id;
data->IrqNumber = pdev->irq;
data->BaseAddress = pci_resource_start(pdev, 0);
data->NumAddress = pci_resource_len(pdev, 0);
data->MmioAddress = (unsigned long)ioremap_nocache(
pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
pci_set_master(pdev);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2))
scsi_register_host(&driver_template);
#else
scsi_register_module(MODULE_SCSI_HA, &driver_template);
#endif
nsp32_msg(KERN_INFO, "nsp32 irq: %i mmio: 0x%lx slot: %s model: %s",
pdev->irq, data->MmioAddress, pdev->slot_name,
nsp32_model[id->driver_data]);
nsp32_dbg(NSP32_DEBUG_REGISTER, "exit");
return 0;
}
static void __devexit nsp32_remove(struct pci_dev *pdev)
{
nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2))
scsi_unregister_host(&driver_template);
#else
scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
#endif
}
static struct pci_device_id nsp32_pci_table[] __devinitdata = {
{
vendor: PCI_VENDOR_ID_IODATA,
device: PCI_DEVICE_ID_NINJASCSI_32BI_CBSC_II,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: MODEL_IODATA,
},
{
vendor: PCI_VENDOR_ID_WORKBIT,
device: PCI_DEVICE_ID_NINJASCSI_32BI_KME,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: MODEL_KME,
},
{
vendor: PCI_VENDOR_ID_WORKBIT,
device: PCI_DEVICE_ID_NINJASCSI_32BI_WBT,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: MODEL_WORKBIT,
},
{
vendor: PCI_VENDOR_ID_WORKBIT,
device: PCI_DEVICE_ID_WORKBIT_STANDARD,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: MODEL_PCI_WORKBIT,
},
{
vendor: PCI_VENDOR_ID_WORKBIT,
device: PCI_DEVICE_ID_NINJASCSI_32BI_LOGITEC,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: MODEL_EXT_ROM,
},
{
vendor: PCI_VENDOR_ID_WORKBIT,
device: PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: MODEL_PCI_LOGITEC,
},
{
vendor: PCI_VENDOR_ID_WORKBIT,
device: PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: MODEL_PCI_MELCO,
},
{0,0,},
};
MODULE_DEVICE_TABLE(pci, nsp32_pci_table);
static struct pci_driver nsp32_driver = {
.name = "nsp32",
.id_table = nsp32_pci_table,
.probe = nsp32_probe,
.remove = nsp32_remove,
#ifdef CONFIG_PM
/* .suspend = nsp32_suspend,*/
/* .resume = nsp32_resume,*/
#endif
};
static int __init init_nsp32(void) {
return pci_module_init(&nsp32_driver);
}
static void __exit exit_nsp32(void) {
pci_unregister_driver(&nsp32_driver);
}
module_init(init_nsp32);
module_exit(exit_nsp32);
/*
* Reset parameters and call scsi_done for data->curlunt.
* Be careful setting SCpnt->result = DID_* before calling this function.
*/
static void nsp32_scsi_done(nsp32_hw_data *data, Scsi_Cmnd *SCpnt)
{
unsigned int base = data->BaseAddress;
/*
* unmap pci
*/
if (SCpnt->request_bufflen == 0) {
goto skip;
}
if (SCpnt->use_sg) {
pci_unmap_sg(data->Pci,
(struct scatterlist *)SCpnt->buffer,
SCpnt->use_sg,
scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
} else {
pci_unmap_single(data->Pci,
(u32)SCpnt->SCp.have_data_in,
SCpnt->request_bufflen,
scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
}
skip:
/*
* clear TRANSFERCONTROL_BM_START
*/
nsp32_write2(base, TRANSFER_CONTROL, 0);
nsp32_write4(base, BM_CNT, 0);
/*
* call scsi_done
*/
(*SCpnt->scsi_done)(SCpnt);
/*
* reset parameters
*/
data->curlunt->SCpnt = NULL;
data->curlunt = NULL;
data->curtarget = NULL;
data->CurrentSC = NULL;
}
/*
* Bus Free Occur
*
* Current Phase is BUSFREE. AutoSCSI is automatically execute BUSFREE phase
* with ACK reply when below condition is matched:
* MsgIn 00: Command Complete.
* MsgIn 02: Save Data Pointer.
* MsgIn 04: Diconnect.
* In other case, unexpected BUSFREE is detected.
*/
static int nsp32_busfree_occur(nsp32_hw_data *data, unsigned short execph)
{
Scsi_Cmnd *SCpnt = data->curlunt->SCpnt;
unsigned int base = data->BaseAddress;
nsp32_dbg(NSP32_DEBUG_BUSFREE, "enter");
nsp32_write4(base, BM_CNT, 0);
nsp32_write2(base, TRANSFER_CONTROL, 0);
/*
* MsgIn 02: Save Data Pointer
*
* VALID:
* Save Data Pointer is received. Adjust pointer.
*
* NO-VALID:
* SCSI-3 says if Save Data Pointer is not received, then we restart
* processing and we can't adjust any SCSI data pointer in next data
* phase.
*/
if (execph & MSGIN_02_VALID) {
nsp32_dbg(NSP32_DEBUG_BUSFREE, "MsgIn02_Valid");
/*
* Check sack_cnt/saved_sack_cnt, then adjust sg table if
* needed.
*/
if (!(execph & MSGIN_00_VALID) &&
((execph & DATA_IN_PHASE) || (execph & DATA_OUT_PHASE))) {
unsigned int sacklen, s_sacklen;
/*
* Read SACK count and SAVEDSACK count, then compare.
*/
sacklen = nsp32_read4(base, SACK_CNT);
s_sacklen = nsp32_read4(base, SAVED_SACK_CNT);
/*
* If SAVEDSACKCNT == 0, it means SavedDataPointer is
* come after data transfering.
*/
if (s_sacklen > 0) {
/*
* Comparing between sack and savedsack to
* check the condition of AutoMsgIn03.
*
* If they are same, set msgin03 == TRUE,
* COMMANDCONTROL_AUTO_MSGIN_03 is enabled at
* reselection. On the other hand, if they
* aren't same, set msgin03 == FALSE, and
* COMMANDCONTROL_AUTO_MSGIN_03 is disabled at
* reselection.
*/
if (sacklen != s_sacklen) {
data->curlunt->msgin03 = FALSE;
} else {
data->curlunt->msgin03 = TRUE;
}
nsp32_adjust_busfree(data, s_sacklen);
}
}
/* This value has not substitude with valid value yet... */
//data->curlunt->save_datp = data->cur_datp;
} else {
/*
* no processing.
*/
}
if (execph & MSGIN_03_VALID) {
/* MsgIn03 was valid to be processed. No need processing. */
}
/*
* target SDTR check
*/
if (data->curtarget->sync_flag & SDTR_INITIATOR) {
/*
* SDTR negotiation pulled by the initiator has not
* finished yet. Fall back to ASYNC mode.
*/
nsp32_set_async(data, data->curtarget);
data->curtarget->sync_flag &= ~SDTR_INITIATOR;
data->curtarget->sync_flag |= SDTR_DONE;
} else if (data->curtarget->sync_flag & SDTR_TARGET) {
/*
* SDTR negotiation pulled by the target has been
* negotiating.
*/
if (execph & (MSGIN_00_VALID | MSGIN_04_VALID)) {
/*
* If valid message is received, then
* negotiation is succeeded.
*/
} else {
/*
* On the contrary, if unexpected bus free is
* occured, then negotiation is failed. Fall
* back to ASYNC mode.
*/
nsp32_set_async(data, data->curtarget);
}
data->curtarget->sync_flag &= ~SDTR_TARGET;
data->curtarget->sync_flag |= SDTR_DONE;
}
/*
* It is always ensured by SCSI standard that initiator
* switches into Bus Free Phase after
* receiving message 00 (Command Complete), 04 (Disconnect).
* It's the reason that processing here is valid.
*/
if (execph & MSGIN_00_VALID) {
/* MsgIn 00: Command Complete */
nsp32_dbg(NSP32_DEBUG_BUSFREE, "command complete");
SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
SCpnt->SCp.Message = 0;
nsp32_dbg(NSP32_DEBUG_BUSFREE,
"normal end stat=0x%x resid=0x%x\n",
SCpnt->SCp.Status, SCpnt->resid);
SCpnt->result =
(DID_OK << 16) | (SCpnt->SCp.Message << 8) | (SCpnt->SCp.Status << 0);
nsp32_scsi_done(data, SCpnt);
/* All operation is done */
return (TRUE);
} else if (execph & MSGIN_04_VALID) {
/* MsgIn 04: Disconnect */
SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
SCpnt->SCp.Message = 4;
nsp32_dbg(NSP32_DEBUG_BUSFREE, "disconnect");
return (TRUE);
} else {
/* Unexpected bus free */
nsp32_msg(KERN_WARNING, "unexpected bus free occured");
/* DID_ERROR? */
//SCpnt->result = (DID_OK << 16) | (SCpnt->SCp.Message << 8) | (SCpnt->SCp.Status << 0);
SCpnt->result = DID_ERROR << 16;
nsp32_scsi_done(data, SCpnt);
return (TRUE);
}
return (FALSE);
}
/*
* nsp32_adjust_busfree - adjusting SG table
*
* Note: This driver adjust the SG table using SCSI ACK
* counter instead of BMCNT counter!
*/
static void nsp32_adjust_busfree(nsp32_hw_data *data, unsigned int s_sacklen)
{
int old_entry = data->cur_entry;
int new_entry;
struct nsp32_sgtable *sgt = data->curlunt->sglun->sgt;
unsigned int restlen, sentlen;
int sg_num = data->curlunt->sg_num;
/* adjust saved SACK count with 4 byte start address boundary */
s_sacklen -= sgt[old_entry].addr & 3;
/*
* calculate new_entry from sack count and each sgt[].len
* calculate the byte which is intent to send
*/
sentlen = 0;
for (new_entry = old_entry; new_entry < sg_num; new_entry++) {
sentlen += (sgt[new_entry].len & ~NSP32_SG_END_SGT);
if (sentlen > s_sacklen) {
break;
}
}
/* all sgt is processed */
if (new_entry == sg_num) {
goto last;
}
if (sentlen == s_sacklen) {
/* XXX: confirm it's ok or not */
/* In this case, it's ok because we are at
the head element of the sg. restlen is correctly calculated. */
}
/* calculate the rest length for transfering */
restlen = sentlen - s_sacklen;
/* update adjusting current SG table entry */
sgt[new_entry].addr += (sgt[new_entry].len - restlen);
sgt[new_entry].len = restlen;
/* set cur_entry with new_entry */
data->cur_entry = new_entry;
return;
last:
/* update hostdata and lun */
return;
}
/*
* It's called MsgOut phase occur.
* NinjaSCSI-32Bi/UDE automatically processes up to 3 messages in
* message out phase. It, however, has more than 3 messages,
* HBA creates the interrupt and we have to process by hand.
*/
static void nsp32_msgout_occur(nsp32_hw_data *data)
{
unsigned int base = data->BaseAddress;
unsigned short command;
long new_sgtp;
int i;
nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR,
"enter: msgoutlen: 0x%x", data->msgoutlen);
/*
* If MsgOut phase is occured without having any
* message, then No_Operation is sent (SCSI-2).
*/
if (data->msgoutlen == 0) {
nsp32_build_nop(data);
}
/*
* Set SGTP ADDR current entry for restarting AUTOSCSI,
* because SGTP is incremented next point.
* There is few statement in the specification...
*/
new_sgtp = data->curlunt->sglun_paddr
+ data->curlunt->cur_entry * sizeof(struct nsp32_sgtable);
/*
* send messages
*/
for (i=0; i<data->msgoutlen; i++) {
nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR,
"%d : 0x%x", i, data->msgoutbuf[i]);
/*
* Check REQ is asserted.
*/
nsp32_wait_req(data, ASSERT);
if (i == (data->msgoutlen - 1)) {
/*
* If the last message, set the AutoSCSI restart
* before send back the ack message. AutoSCSI
* restart automatically negate ATN signal.
*/
//command = (AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02);
//nsp32_restart_autoscsi(data, command);
nsp32_write2(base, COMMAND_CONTROL,
(CLEAR_CDB_FIFO_POINTER |
AUTO_COMMAND_PHASE |
AUTOSCSI_RESTART |
AUTO_MSGIN_00_OR_04 |
AUTO_MSGIN_02 ));
}
/*
* Write data with SACK, then wait sack is
* automatically negated.
*/
nsp32_write1(base, SCSI_DATA_WITH_ACK, data->msgoutbuf[i]);
nsp32_wait_sack(data, NEGATE);
nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR, "bus: 0x%x\n",
nsp32_read1(base, SCSI_BUS_MONITOR));
};
data->msgoutlen = 0;
nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR, "exit");
}
/*
* Restart AutoSCSI
*
* Note: Restarting AutoSCSI needs set:
* SYNC_REG, ACK_WIDTH, SGT_ADR, TRANSFER_CONTROL
*/
static void nsp32_restart_autoscsi(nsp32_hw_data *data, unsigned short command)
{
unsigned int base = data->BaseAddress;
unsigned short transfer = 0;
Scsi_Cmnd *SCpnt = data->curlunt->SCpnt;
nsp32_dbg(NSP32_DEBUG_RESTART, "enter");
if (data->curtarget == NULL || data->curlunt == NULL) {
nsp32_msg(KERN_ERR, "Target or Lun is invalid");
}
/*
* set SYNC_REG
* Don't set BM_START_ADR before setting this register.
*/
nsp32_write1(base, SYNC_REG, data->curtarget->syncreg);
/*
* set ACKWIDTH
*/
nsp32_write1(base, ACK_WIDTH, data->curtarget->ackwidth);
/*
* set SGT ADDR (physical address)
*/
nsp32_write4(base, SGT_ADR, data->curlunt->sglun_paddr);
/*
* set TRANSFER CONTROL REG
*/
transfer = 0;
transfer |= (TRANSFER_GO | ALL_COUNTER_CLR);
if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
if (SCpnt->request_bufflen > 0) {
transfer |= BM_START;
}
} else if (data->trans_method & NSP32_TRANSFER_MMIO) {
transfer |= CB_MMIO_MODE;
} else if (data->trans_method & NSP32_TRANSFER_PIO) {
transfer |= CB_IO_MODE;
}
nsp32_write2(base, TRANSFER_CONTROL, transfer);
/*
* restart AutoSCSI
*
* TODO: COMMANDCONTROL_AUTO_COMMAND_PHASE is needed ?
*/
command |= (CLEAR_CDB_FIFO_POINTER |
AUTO_COMMAND_PHASE |
AUTOSCSI_RESTART);
nsp32_write2(base, COMMAND_CONTROL, command);
nsp32_dbg(NSP32_DEBUG_RESTART, "exit");
}
/*
* cannot run automatically message in occur
*/
static void nsp32_msgin_occur(nsp32_hw_data *data, unsigned long irq_status,
unsigned short execph)
{
unsigned int base = data->BaseAddress;
unsigned char msg;
unsigned char msgtype;
unsigned char newlun;
unsigned short command = 0;
int msgclear = TRUE;
long new_sgtp;
int ret;
/*
* read first message
* Use SCSIDATA_W_ACK instead of SCSIDATAIN, because the procedure
* of Message-In have to be processed before sending back SCSI ACK.
*/
msg = nsp32_read1(base, SCSI_DATA_IN);
data->msginbuf[(unsigned char)data->msginlen] = msg;
msgtype = data->msginbuf[0];
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR,
"enter: msglen: 0x%x msgin: 0x%x msgtype: 0x%x",
data->msginlen, msg, msgtype);
/*
* TODO: We need checking whether bus phase is message in?
*/
/*
* assert SCSI ACK
*/
nsp32_sack_assert(data);
/*
* processing IDENTIFY
*/
if (msgtype & 0x80) {
if (!(irq_status & IRQSTATUS_RESELECT_OCCUER)) {
/* Invalid (non reselect) phase */
goto reject;
}
newlun = msgtype & 0x1f; /* TODO: SPI-3 compliant? */
ret = nsp32_reselection(data, newlun);
if (ret == TRUE) {
goto restart;
} else {
goto reject;
}
}
/*
* processing messages except for IDENTIFY
*
* TODO: Messages are all SCSI-2 terminology. SCSI-3 compliance is TODO.
*/
switch (msgtype) {
/*
* 1-byte message
*/
case COMMAND_COMPLETE:
case DISCONNECT:
/*
* These messages should not be occured.
* They should be processed on AutoSCSI sequencer.
*/
nsp32_msg(KERN_WARNING,
"unexpected message of AutoSCSI MsgIn: 0x%x", msg);
break;
case RESTORE_POINTERS:
/*
* AutoMsgIn03 is disabled, and HBA gets this message.
*/
if ((execph & DATA_IN_PHASE) || (execph & DATA_OUT_PHASE)) {
unsigned int s_sacklen;
s_sacklen = nsp32_read4(base, SAVED_SACK_CNT);
if ((execph & MSGIN_02_VALID) && (s_sacklen > 0)) {
nsp32_adjust_busfree(data, s_sacklen);
} else {
/* No need to rewrite SGT */
}
}
data->curlunt->msgin03 = FALSE;
/* Update with the new value */
/* reset SACK/SavedACK counter (or ALL clear?) */
nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
/*
* set new sg pointer
*/
new_sgtp = data->curlunt->sglun_paddr +
data->curlunt->cur_entry * sizeof(struct nsp32_sgtable);
nsp32_write4(base, SGT_ADR, new_sgtp);
break;
case SAVE_POINTERS:
/*
* These messages should not be occured.
* They should be processed on AutoSCSI sequencer.
*/
nsp32_msg (KERN_WARNING,
"unexpected message of AutoSCSI MsgIn: SAVE_POINTERS");
break;
case MESSAGE_REJECT:
/* If previous message_out is sending SDTR, and get
message_reject from target, SDTR negotiation is failed */
if (data->curtarget->sync_flag &
(SDTR_INITIATOR | SDTR_TARGET)) {
/*
* Current target is negotiating SDTR, but it's
* failed. Fall back to async transfer mode, and set
* SDTR_DONE.
*/
nsp32_set_async(data, data->curtarget);
data->curtarget->sync_flag &= ~SDTR_INITIATOR;
data->curtarget->sync_flag |= SDTR_DONE;
}
break;
case LINKED_CMD_COMPLETE:
case LINKED_FLG_CMD_COMPLETE:
/* queue tag is not supported currently */
nsp32_msg (KERN_WARNING,
"unsupported message: 0x%x", msgtype);
break;
case INITIATE_RECOVERY:
/* staring ECA (Extended Contingent Allegiance) state. */
/* This message is declined in SPI2 or later. */
goto reject;
/*
* 2-byte message
*/
case SIMPLE_QUEUE_TAG:
case 0x23:
/*
* 0x23: Ignore_Wide_Residue is not declared in scsi.h.
* No support is needed.
*/
if (data->msginlen >= 1) {
goto reject;
}
/* current position is 1-byte of 2 byte */
msgclear = FALSE;
break;
/*
* extended message
*/
case EXTENDED_MESSAGE:
if (data->msginlen < 1) {
/*
* Current position does not reach 2-byte
* (2-byte is extended message length).
*/
msgclear = FALSE;
break;
}
if ((data->msginbuf[1] + 1) > data->msginlen) {
/*
* Current extended message has msginbuf[1] + 2
* (msginlen starts counting from 0, so buf[1] + 1).
* If current message position is not finished,
* continue receiving message.
*/
msgclear = FALSE;
break;
}
/*
* Reach here means regular length of each type of
* extended messages.
*/
switch (data->msginbuf[2]) {
case EXTENDED_MODIFY_DATA_POINTER:
/* TODO */
goto reject; /* not implemented yet */
break;
case EXTENDED_SDTR:
/*
* Exchange this message between initiator and target.
*/
if (data->msginlen != EXTENDED_SDTR_LEN + 1) {
/*
* received inappropriate message.
*/
goto reject;
break;
}
nsp32_analyze_sdtr(data);
break;
case EXTENDED_EXTENDED_IDENTIFY:
/* SCSI-I only, not supported. */
goto reject; /* not implemented yet */
break;
case EXTENDED_WDTR:
goto reject; /* not implemented yet */
break;
default:
goto reject;
}
break;
default:
goto reject;
}
restart:
if (msgclear == TRUE) {
data->msginlen = 0;
/*
* If restarting AutoSCSI, but there are some message to out
* (msgoutlen > 0), set AutoATN, and set SCSIMSGOUT as 0
* (MV_VALID = 0). When commandcontrol is written with
* AutoSCSI restart, at the same time MsgOutOccur should be
* happened (however, such situation is really possible...?).
*/
if (data->msgoutlen > 0) {
nsp32_write4(base, SCSI_MSG_OUT, 0);
command |= AUTO_ATN;
}
/*
* restart AutoSCSI
* If it's failed, COMMANDCONTROL_AUTO_COMMAND_PHASE is needed.
*/
command |= (AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02);
/*
* If current msgin03 is TRUE, then flag on.
*/
if (data->curlunt->msgin03 == TRUE) {
command |= AUTO_MSGIN_03;
}
data->curlunt->msgin03 = FALSE;
} else {
data->msginlen++;
}
/*
* restart AutoSCSI
*/
nsp32_restart_autoscsi(data, command);
/*
* wait SCSI REQ negate for REQ-ACK handshake
*/
nsp32_wait_req(data, NEGATE);
/*
* negate SCSI ACK
*/
nsp32_sack_negate(data);
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit");
return;
reject:
nsp32_msg(KERN_WARNING,
"invalid or unsupported MessageIn, rejected. "
"current msg: 0x%x (len: 0x%x), processing msg: 0x%x",
msg, data->msginlen, msgtype);
nsp32_build_reject(data);
data->msginlen = 0;
goto restart;
}
/*
*
*/
static void nsp32_analyze_sdtr(nsp32_hw_data *data)
{
struct nsp32_target *target = data->curtarget;
struct nsp32_sync_table *synct;
unsigned char get_period = data->msginbuf[3];
unsigned char get_offset = data->msginbuf[4];
int entry;
int syncnum;
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "enter");
synct = data->synct;
syncnum = data->syncnum;
/*
* If this inititor sent the SDTR message, then target responds SDTR,
* initiator SYNCREG, ACKWIDTH from SDTR parameter.
* Messages are not appropriate, then send back reject message.
* If initiator did not send the SDTR, but target sends SDTR,
* initiator calculator the appropriate parameter and send back SDTR.
*/
if (target->sync_flag & SDTR_INITIATOR) {
/*
* Initiator sent SDTR, the target responds and
* send back negotiation SDTR.
*/
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target responds SDTR");
target->sync_flag &= ~SDTR_INITIATOR;
target->sync_flag |= SDTR_DONE;
/*
* offset:
*/
if (get_offset > SYNC_OFFSET) {
/*
* Negotiation is failed, the target send back
* unexpected offset value.
*/
goto reject;
}
if (get_offset == ASYNC_OFFSET) {
/*
* Negotiation is succeeded, the target want
* to fall back into asynchronous transfer mode.
*/
goto async;
}
/*
* period:
* Check whether sync period is too short. If too short,
* fall back to async mode. If it's ok, then investigate
* the received sync period. If sync period is acceptable
* between sync table start_period and end_period, then
* set this I_T nexus as sent offset and period.
* If it's not acceptable, send back reject and fall back
* to async mode.
*/
if (get_period < data->synct[0].period_num) {
/*
* Negotiation is failed, the target send back
* unexpected period value.
*/
goto reject;
}
entry = nsp32_search_period_entry(data, target, get_period);
if (entry < 0) {
/*
* Target want to use long period which is not
* acceptable NinjaSCSI-32Bi/UDE.
*/
goto reject;
}
/*
* Set new sync table and offset in this I_T nexus.
*/
nsp32_set_sync_entry(data, target, entry, get_offset);
} else {
/* Target send SDTR to initiator. */
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target send SDTR");
target->sync_flag |= SDTR_INITIATOR;
/* offset: */
if (get_offset > SYNC_OFFSET) {
/* send back as SYNC_OFFSET */
get_offset = SYNC_OFFSET;
}
/* period: */
if (get_period < data->synct[0].period_num) {
get_period = data->synct[0].period_num;
}
entry = nsp32_search_period_entry(data, target, get_period);
if (get_offset == ASYNC_OFFSET || entry < 0) {
nsp32_set_async(data, target);
nsp32_build_sdtr(data, 0, ASYNC_OFFSET);
} else {
nsp32_set_sync_entry(data, target, entry, get_offset);
nsp32_build_sdtr(data, get_period, get_offset);
}
}
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit");
return;
reject:
/*
* If the current message is unacceptable, send back to the target
* with reject message.
*/
nsp32_build_reject(data);
async:
nsp32_set_async(data, target); /* set as ASYNC transfer mode */
nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit: set async");
return;
}
/*
* Search config entry number matched in sync_table from given
* target and speed period value. If failed to search, return negative value.
*/
static int nsp32_search_period_entry(nsp32_hw_data *data,
struct nsp32_target *target,
unsigned char period)
{
int i;
if (target->limit_entry >= data->syncnum) {
nsp32_msg(KERN_ERR, "limit_entry exceeds syncnum!");
target->limit_entry = 0;
}
for (i=target->limit_entry; i<data->syncnum; i++) {
if (period >= data->synct[i].start_period &&
period <= data->synct[i].end_period) {
break;
}
}
/*
* Check given period value is over the sync_table value.
* If so, return max value.
*/
if (i == data->syncnum) {
i = -1;
}
return i;
}
/*
* target <-> initiator use ASYNC transfer
*/
static void nsp32_set_async(nsp32_hw_data *data, struct nsp32_target *target)
{
unsigned char period = data->synct[target->limit_entry].period_num;
target->offset = ASYNC_OFFSET;
target->syncreg = TO_SYNCREG(period, ASYNC_OFFSET);
target->ackwidth = 0;
nsp32_dbg(NSP32_DEBUG_SYNC, "set async");
}
/*
* target <-> initiator use maximum SYNC transfer
*/
static void nsp32_set_max_sync(nsp32_hw_data *data,
struct nsp32_target *target,
unsigned char *period, unsigned char *offset)
{
unsigned char period_num, ackwidth;
period_num = data->synct[target->limit_entry].period_num;
*period = data->synct[target->limit_entry].start_period;
ackwidth = data->synct[target->limit_entry].ackwidth;
*offset = SYNC_OFFSET;
target->syncreg = TO_SYNCREG(period_num, *offset);
target->ackwidth = ackwidth;
target->offset = *offset;
}
/*
* target <-> initiator use entry number speed
*/
static void nsp32_set_sync_entry(nsp32_hw_data *data,
struct nsp32_target *target,
int entry, unsigned char offset)
{
unsigned char period, ackwidth;
period = data->synct[entry].period_num;
ackwidth = data->synct[entry].ackwidth;
offset = offset;
target->syncreg = TO_SYNCREG(period, offset);
target->ackwidth = ackwidth;
target->offset = offset;
nsp32_dbg(NSP32_DEBUG_SYNC, "set sync");
}
/*
* It waits until SCSI REQ becomes assertion or negation state.
*
* Note: If nsp32_msgin_occur is called, we asserts SCSI ACK. Then
* connected target responds SCSI REQ negation. We have to wait
* SCSI REQ becomes negation in order to negate SCSI ACK signal for
* REQ-ACK handshake.
*/
static void nsp32_wait_req(nsp32_hw_data *data, int state)
{
unsigned int base = data->BaseAddress;
int wait_time = 0;
unsigned char bus;
if (!((state == ASSERT) || (state == NEGATE))) {
nsp32_msg(KERN_ERR, "unknown state designation");
}
state <<= 5; /* REQ is BIT(5) */
do {
bus = nsp32_read1(base, SCSI_BUS_MONITOR);
if ((bus & BUSMON_REQ) == state) {
nsp32_dbg(NSP32_DEBUG_WAIT,
"wait_time: %d", wait_time);
return;
}
udelay(1);
wait_time++;
} while (wait_time < REQSACK_TIMEOUT_TIME);
nsp32_msg(KERN_WARNING, "wait REQ timeout, state: %d", state);
}
/*
* It waits until SCSI SACK becomes assertion or negation state.
*/
static void nsp32_wait_sack(nsp32_hw_data *data, int state)
{
unsigned int base = data->BaseAddress;
int wait_time = 0;
unsigned char bus;
if (!((state == ASSERT) || (state == NEGATE))) {
nsp32_msg(KERN_ERR, "unknown state designation");
}
state <<= 4; /* ACK is BIT(4) */
do {
bus = nsp32_read1(base, SCSI_BUS_MONITOR);
if ((bus & BUSMON_ACK) == state) {
nsp32_dbg(NSP32_DEBUG_WAIT,
"wait_time: %d", wait_time);
return;
}
udelay(1);
wait_time++;
} while (wait_time < REQSACK_TIMEOUT_TIME);
nsp32_msg(KERN_WARNING, "wait SACK timeout, state: %d", state);
}
/*
* assert SCSI ACK
*
* Note: SCSI ACK assertion needs with ACKENB=1, AUTODIRECTION=1.
*/
static void nsp32_sack_assert(nsp32_hw_data *data)
{
unsigned char busctrl;
unsigned int base = data->BaseAddress;
busctrl = nsp32_read1(base, SCSI_BUS_CONTROL);
busctrl |= (BUSCTL_ACK | AUTODIRECTION | ACKENB);
nsp32_write1(base, SCSI_BUS_CONTROL,busctrl);
}
/*
* negate SCSI ACK
*/
static void nsp32_sack_negate(nsp32_hw_data *data)
{
unsigned char busctrl;
unsigned int base = data->BaseAddress;
busctrl = nsp32_read1(base, SCSI_BUS_CONTROL);
busctrl &= ~BUSCTL_ACK;
nsp32_write1(base, SCSI_BUS_CONTROL, busctrl);
}
/*
* getting EEPROM parameter
*/
static int nsp32_getprom_param(nsp32_hw_data *data)
{
int vendor = data->pci_devid->vendor;
int device = data->pci_devid->device;
int ret, val, i;
/*
* EEPROM checking.
*/
ret = nsp32_prom_read(data, 0x7e);
if (ret != 0x55) {
nsp32_msg(KERN_INFO, "No EEPROM detected: 0x%x", ret);
return (FALSE);
}
ret = nsp32_prom_read(data, 0x7f);
if (ret != 0xaa) {
nsp32_msg(KERN_INFO, "Invalid number: 0x%x", ret);
return (FALSE);
}
/*
* check EEPROM type
*/
if (vendor == PCI_VENDOR_ID_WORKBIT &&
device == PCI_DEVICE_ID_WORKBIT_STANDARD) {
ret = nsp32_getprom_standard(data);
} else if (vendor == PCI_VENDOR_ID_WORKBIT &&
device == PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC) {
ret = nsp32_getprom_new(data);
} else if (vendor == PCI_VENDOR_ID_WORKBIT &&
device == PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO ) {
ret = nsp32_getprom_new(data);
} else {
nsp32_msg(KERN_WARNING, "Unknown EEPROM");
ret = FALSE;
}
/* for debug : SPROM data full checking */
for (i=0; i<=0x1f; i++) {
val = nsp32_prom_read(data, i);
nsp32_dbg(NSP32_DEBUG_EEPROM,
"rom address 0x%x : 0x%x", i, val);
}
return (ret);
}
/*
* AT24C01A (Logitec: LHA-600S), AT24C02 (Melco Buffalo: IFC-USLP) data map:
*
* ROMADDR
* 0x00 - 0x06 : Device Synchronous Transfer Period (SCSI ID 0 - 6)
* Value 0x0: ASYNC, 0x0c: Ultra-20M, 0x19: Fast-10M
* 0x07 : HBA Synchronous Transfer Period
* Value 0: AutoSync, 1: Manual Setting
* 0x08 - 0x0f : Not Used? (0x0)
* 0x10 : Bus Termination
* Value 0: Auto[ON], 1: ON, 2: OFF
* 0x11 : Not Used? (0)
* 0x12 : Bus Reset Delay Time (0x03)
* 0x13 : Bootable CD Support
* Value 0: Disable, 1: Enable
* 0x14 : Device Scan
* Bit 7 6 5 4 3 2 1 0
* | <----------------->
* | SCSI ID: Value 0: Skip, 1: YES
* |-> Value 0: ALL scan, Value 1: Manual
* 0x15 - 0x1b : Not Used? (0)
* 0x1c : Constant? (0x01) (clock div?)
* 0x1d - 0x7c : Not Used (0xff)
* 0x7d : Not Used? (0xff)
* 0x7e : Constant (0x55), HBA chip revision
* 0x7f : Constant (0xaa), HBA value
*/
static int nsp32_getprom_new(nsp32_hw_data *data)
{
int ret, i;
int auto_sync;
struct nsp32_target *target;
int entry;
/*
* Reset time which is designated by EEPROM.
*
* TODO: Not used yet.
*/
data->resettime = nsp32_prom_read(data, 0x12);
/*
* HBA Synchronous Transfer Period
*
* Note: auto_sync = 0: auto, 1: manual. Ninja SCSI HBA spec says
* that if auto_sync is 0 (auto), and connected SCSI devices are
* same or lower than 3, then transfer speed is set as ULTRA-20M.
* On the contrary if connected SCSI devices are same or higher
* than 4, then transfer speed is set as FAST-10M.
*
* I break this rule. The number of connected SCSI devices are
* only ignored. If auto_sync is 0 (auto), then transfer speed is
* forced as ULTRA-20M.
*/
ret = nsp32_prom_read(data, 0x07);
switch (ret) {
case 0:
auto_sync = TRUE;
break;
case 1:
auto_sync = FALSE;
break;
default:
nsp32_msg(KERN_WARNING,
"Unsupported Auto Sync mode."
"Fall back to manual mode.");
auto_sync = TRUE;
}
if (trans_mode == ULTRA20M_MODE) {
auto_sync = TRUE;
}
/*
* each device Synchronous Transfer Period
*/
for (i=0; i<NSP32_HOST_SCSIID; i++) {
target = &data->target[i];
if (auto_sync == TRUE) {
target->limit_entry = 0; /* set as ULTRA20M */
} else {
ret = nsp32_prom_read(data, i);
entry = nsp32_search_period_entry(data, target, ret);
if (entry < 0) {
/* search failed... set maximum speed */
entry = 0;
}
target->limit_entry = entry;
}
}
return (TRUE);
}
/*
* ? (I-O Data: SC-NBD) data map:
*
* ROMADDR
* 0x00 - 0x06 : Device Synchronous Transfer Period (SCSI ID 0 - 6)
* Value 0x0: 20MB/S, 0x1: 10MB/S, 0x2: 5MB/S, 0x3: ASYNC
* 0x07 : 0 (HBA Synchronous Transfer Period: Auto Sync)
* 0x08 - 0x0f : Not Used? (0x0)
* 0x10 : Transfer Mode
* Value 0: PIO, 1: Busmater
* 0x11 : Bus Reset Delay Time (0x00-0x20)
* 0x12 : Bus Termination
* Value 0: Disable, 1: Enable
* 0x13 - 0x19 : Disconnection
* Value 0: Disable, 1: Enable
* 0x1a - 0x7c : Not Used? (0)
* 0x7d : Not Used? (0xf8)
* 0x7e : Constant (0x55), HBA chip revision
* 0x7f : Constant (0xaa), HBA value
*/
static int nsp32_getprom_standard(nsp32_hw_data *data)
{
int ret, i;
struct nsp32_target *target;
int entry, val;
/*
* Reset time which is designated by EEPROM.
*
* TODO: Not used yet.
*/
data->resettime = nsp32_prom_read(data, 0x11);
/*
* each device Synchronous Transfer Period
*/
for (i=0; i<NSP32_HOST_SCSIID; i++) {
target = &data->target[i];
ret = nsp32_prom_read(data, i);
switch (ret) {
case 0: /* 20MB/s */
val = 0x0c;
break;
case 1: /* 10MB/s */
val = 0x19;
break;
case 2: /* 5MB/s */
val = 0x32;
break;
case 3: /* ASYNC */
val = 0x0;
break;
default: /* default 20MB/s */
val = 0x0c;
}
entry = nsp32_search_period_entry(data, target, val);
if (entry < 0 || trans_mode == ULTRA20M_MODE) {
/* search failed... set maximum speed */
entry = 0;
}
target->limit_entry = entry;
}
return (TRUE);
}
/*
* Atmel AT24C01A (drived in 5V) serial EEPROM routines
*/
static int nsp32_prom_read(nsp32_hw_data *data, int romaddr)
{
int i, val;
/* start condition */
nsp32_prom_start(data);
/* device address */
nsp32_prom_write(data, 1); /* 1 */
nsp32_prom_write(data, 0); /* 0 */
nsp32_prom_write(data, 1); /* 1 */
nsp32_prom_write(data, 0); /* 0 */
nsp32_prom_write(data, 0); /* A2: 0 (GND) */
nsp32_prom_write(data, 0); /* A1: 0 (GND) */
nsp32_prom_write(data, 0); /* A0: 0 (GND) */
/* R/W: W for dummy write */
nsp32_prom_write(data, 0);
/* ack */
nsp32_prom_write(data, 0);
/* word address */
for (i=7; i>=0; i--) {
nsp32_prom_write(data, ((romaddr >> i) & 1));
}
/* ack */
nsp32_prom_write(data, 0);
/* start condition */
nsp32_prom_start(data);
/* device address */
nsp32_prom_write(data, 1); /* 1 */
nsp32_prom_write(data, 0); /* 0 */
nsp32_prom_write(data, 1); /* 1 */
nsp32_prom_write(data, 0); /* 0 */
nsp32_prom_write(data, 0); /* A2: 0 (GND) */
nsp32_prom_write(data, 0); /* A1: 0 (GND) */
nsp32_prom_write(data, 0); /* A0: 0 (GND) */
/* R/W: R */
nsp32_prom_write(data, 1);
/* ack */
nsp32_prom_write(data, 0);
/* data... */
val = 0;
for (i=7; i>=0; i--) {
val += (nsp32_prom_fetch(data) << i);
}
/* no ack */
nsp32_prom_write(data, 1);
/* stop condition */
nsp32_prom_stop(data);
return (val);
}
static void nsp32_prom_start (nsp32_hw_data *data)
{
/* start condition */
nsp32_prom_set(data, SCL, 1);
nsp32_prom_set(data, SDA, 1);
nsp32_prom_set(data, ENA, 1); /* output mode */
nsp32_prom_set(data, SDA, 0); /* keeping SCL=1 and transiting
* SDA 1->0 is start condition */
nsp32_prom_set(data, SCL, 0);
}
static void nsp32_prom_stop (nsp32_hw_data *data)
{
/* stop condition */
nsp32_prom_set(data, SCL, 1);
nsp32_prom_set(data, SDA, 0);
nsp32_prom_set(data, ENA, 1); /* output mode */
nsp32_prom_set(data, SDA, 1);
nsp32_prom_set(data, SCL, 0);
}
static void nsp32_prom_write (nsp32_hw_data *data, int val)
{
/* write */
nsp32_prom_set(data, SDA, val);
nsp32_prom_set(data, SCL, 1);
nsp32_prom_set(data, SCL, 0);
}
static int nsp32_prom_fetch (nsp32_hw_data *data)
{
int val;
/* read */
nsp32_prom_set(data, ENA, 0); /* input mode */
nsp32_prom_set(data, SCL, 1);
val = nsp32_prom_get(data, SDA);
nsp32_prom_set(data, SCL, 0);
nsp32_prom_set(data, ENA, 1); /* output mode */
return (val);
}
static inline void nsp32_prom_set(nsp32_hw_data *data, int bit, int val)
{
int cur;
int base = data->BaseAddress;
switch(val) {
case 0:
cur = nsp32_index_read1(base, SERIAL_ROM_CTL);
nsp32_index_write1(base, SERIAL_ROM_CTL, cur & ~bit);
break;
case 1:
cur = nsp32_index_read1(base, SERIAL_ROM_CTL);
nsp32_index_write1(base, SERIAL_ROM_CTL, cur | bit);
break;
default:
nsp32_msg(KERN_ERR, "val must be 0 or 1");
return;
}
udelay(10);
}
static inline int nsp32_prom_get(nsp32_hw_data *data, int bit)
{
int ret;
int base = data->BaseAddress;
ret = nsp32_index_read1(base, SERIAL_ROM_CTL) & bit;
switch (ret) {
case 0:
ret = 0;
break;
case SDA:
ret = 1;
break;
default:
nsp32_msg(KERN_ERR, "return value is not appropriate");
}
udelay(10);
return (ret);
}
/* end */
/*
* Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus SCSI Host Bus Adapter driver
* Basic data header
*
* 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, 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.
*/
#ifndef _NSP32_H
#define _NSP32_H
//#define NSP32_DEBUG 9
/*
* VENDOR/DEVICE ID
*/
#define PCI_VENDOR_ID_IODATA 0x10fc
#define PCI_VENDOR_ID_WORKBIT 0x1145
#define PCI_DEVICE_ID_NINJASCSI_32BI_CBSC_II 0x0005
#define PCI_DEVICE_ID_NINJASCSI_32BI_KME 0xf007
#define PCI_DEVICE_ID_NINJASCSI_32BI_WBT 0x8007
#define PCI_DEVICE_ID_WORKBIT_STANDARD 0xf010
#define PCI_DEVICE_ID_WORKBIT_DUALEDGE 0xf011
#define PCI_DEVICE_ID_NINJASCSI_32BI_LOGITEC 0xf012
#define PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC 0xf013
#define PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO 0xf015
/*
* MODEL
*/
enum {
MODEL_IODATA = 0,
MODEL_KME = 1,
MODEL_WORKBIT = 2,
MODEL_EXT_ROM = 3,
MODEL_PCI_WORKBIT = 4,
MODEL_PCI_LOGITEC = 5,
MODEL_PCI_MELCO = 6,
};
static char * nsp32_model[] = {
"I-O DATA CBSC-II",
"KME SCSI card",
"Workbit duo SCSI card",
"External ROM",
"Workbit Standard/IO Data PCI card",
"Logitec PCI card",
"Melco PCI card",
};
/*
* SCSI Generic Definitions
*/
#define EXTENDED_SDTR_LEN 0x03
/*
* MACRO
*/
#define BIT(x) (1UL << (x))
#ifndef MIN
# define MIN(a,b) ((a) > (b) ? (b) : (a))
#endif
/*
* BASIC Definitions
*/
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
#define ASSERT 1
#define NEGATE 0
/*******************/
/* normal register */
/*******************/
/*
* Don't access below register with Double Word:
* +00, +04, +08, +0c, +64, +80, +84, +88, +90, +c4, +c8, +cc, +d0.
*/
#define IRQ_CONTROL 0x00 /* BASE+00, W, W */
#define IRQ_STATUS 0x00 /* BASE+00, W, R */
# define IRQSTATUS_LATCHED_MSG BIT(0)
# define IRQSTATUS_LATCHED_IO BIT(1)
# define IRQSTATUS_LATCHED_CD BIT(2)
# define IRQSTATUS_LATCHED_BUS_FREE BIT(3)
# define IRQSTATUS_RESELECT_OCCUER BIT(4)
# define IRQSTATUS_PHASE_CHANGE_IRQ BIT(5)
# define IRQSTATUS_SCSIRESET_IRQ BIT(6)
# define IRQSTATUS_TIMER_IRQ BIT(7)
# define IRQSTATUS_FIFO_SHLD_IRQ BIT(8)
# define IRQSTATUS_PCI_IRQ BIT(9)
# define IRQSTATUS_BMCNTERR_IRQ BIT(10)
# define IRQSTATUS_AUTOSCSI_IRQ BIT(11)
# define PCI_IRQ_MASK BIT(12)
# define TIMER_IRQ_MASK BIT(13)
# define FIFO_IRQ_MASK BIT(14)
# define SCSI_IRQ_MASK BIT(15)
# define IRQ_CONTROL_ALL_IRQ_MASK 0xf000
# define IRQSTATUS_ANY_IRQ (IRQSTATUS_RESELECT_OCCUER | \
IRQSTATUS_PHASE_CHANGE_IRQ | \
IRQSTATUS_SCSIRESET_IRQ | \
IRQSTATUS_TIMER_IRQ | \
IRQSTATUS_FIFO_SHLD_IRQ | \
IRQSTATUS_PCI_IRQ | \
IRQSTATUS_BMCNTERR_IRQ | \
IRQSTATUS_AUTOSCSI_IRQ )
#define TRANSFER_CONTROL 0x02 /* BASE+02, W, W */
#define TRANSFER_STATUS 0x02 /* BASE+02, W, R */
# define CB_MMIO_MODE BIT(0)
# define CB_IO_MODE BIT(1)
# define BM_TEST BIT(2)
# define BM_TEST_DIR BIT(3)
# define DUAL_EDGE_ENABLE BIT(4)
# define NO_TRANSFER_TO_HOST BIT(5)
# define TRANSFER_GO BIT(7)
# define BLIEND_MODE BIT(8)
# define BM_START BIT(9)
# define ADVANCED_BM_WRITE BIT(10)
# define BM_SINGLE_MODE BIT(11)
# define FIFO_TRUE_FULL BIT(12)
# define FIFO_TRUE_EMPTY BIT(13)
# define ALL_COUNTER_CLR BIT(14)
# define FIFOTEST BIT(15)
#define INDEX_REG 0x04 /* BASE+04, Byte(R/W), Word(R) */
#define TIMER_SET 0x06 /* BASE+06, W, R/W */
# define TIMER_CNT_MASK 0xff
# define TIMER_STOP BIT(8)
#define DATA_REG_LOW 0x08 /* BASE+08, LowW, R/W */
#define DATA_REG_HI 0x0a /* BASE+0a, Hi-W, R/W */
#define FIFO_REST_CNT 0x0c /* BASE+0c, W, R/W */
# define FIFO_REST_MASK 0x1ff
# define FIFO_EMPTY_SHLD_FLAG BIT(14)
# define FIFO_FULL_SHLD_FLAG BIT(15)
#define SREQ_SMPL_RATE 0x0f /* BASE+0f, B, R/W */
# define SREQSMPLRATE_RATE0 BIT(0)
# define SREQSMPLRATE_RATE1 BIT(1)
# define SAMPLING_ENABLE BIT(2)
#define SCSI_BUS_CONTROL 0x10 /* BASE+10, B, R/W */
# define BUSCTL_SEL BIT(0)
# define BUSCTL_RST BIT(1)
# define BUSCTL_DATAOUT_ENB BIT(2)
# define BUSCTL_ATN BIT(3)
# define BUSCTL_ACK BIT(4)
# define BUSCTL_BSY BIT(5)
# define AUTODIRECTION BIT(6)
# define ACKENB BIT(7)
#define CLR_COUNTER 0x12 /* BASE+12, B, W */
# define ACK_COUNTER_CLR BIT(0)
# define SREQ_COUNTER_CLR BIT(1)
# define FIFO_HOST_POINTER_CLR BIT(2)
# define FIFO_REST_COUNT_CLR BIT(3)
# define BM_COUNTER_CLR BIT(4)
# define SAVED_ACK_CLR BIT(5)
# define CLRCOUNTER_ALLMASK (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5))
#define SCSI_BUS_MONITOR 0x12 /* BASE+12, B, R */
# define BUSMON_MSG BIT(0)
# define BUSMON_IO BIT(1)
# define BUSMON_CD BIT(2)
# define BUSMON_BSY BIT(3)
# define BUSMON_ACK BIT(4)
# define BUSMON_REQ BIT(5)
# define BUSMON_SEL BIT(6)
# define BUSMON_ATN BIT(7)
#define COMMAND_DATA 0x14 /* BASE+14, B, R/W */
#define PARITY_CONTROL 0x16 /* BASE+16, B, R/W */
# define PARITY_CHECK_ENABLE BIT(0)
# define PARITY_ERROR_CLEAR BIT(1)
#define PARITY_STATUS 0x16
//# define PARITY_CHECK_ENABLE BIT(0)
# define PARITY_ERROR_NORMAL BIT(1)
# define PARITY_ERROR_LSB BIT(1)
# define PARITY_ERROR_MSB BIT(2)
#define RESELECT_ID 0x18 /* BASE+18, B, R */
#define COMMAND_CONTROL 0x18 /* BASE+18, W, W */
# define CLEAR_CDB_FIFO_POINTER BIT(0)
# define AUTO_COMMAND_PHASE BIT(1)
# define AUTOSCSI_START BIT(2)
# define AUTOSCSI_RESTART BIT(3)
# define AUTO_PARAMETER BIT(4)
# define AUTO_ATN BIT(5)
# define AUTO_MSGIN_00_OR_04 BIT(6)
# define AUTO_MSGIN_02 BIT(7)
# define AUTO_MSGIN_03 BIT(8)
#define SET_ARBIT 0x1a /* BASE+1a, B, W */
# define ARBIT_GO BIT(0)
# define ARBIT_CLEAR BIT(1)
#define ARBIT_STATUS 0x1a /* BASE+1a, B, R */
//# define ARBIT_GO BIT(0)
# define ARBIT_WIN BIT(1)
# define ARBIT_FAIL BIT(2)
# define AUTO_PARAMETER_VALID BIT(3)
# define SGT_VALID BIT(4)
#define SYNC_REG 0x1c /* BASE+1c, B, R/W */
#define ACK_WIDTH 0x1d /* BASE+1d, B, R/W */
#define SCSI_DATA_WITH_ACK 0x20 /* BASE+20, B, R/W */
#define SCSI_OUT_LATCH_TARGET_ID 0x22 /* BASE+22, B, W */
#define SCSI_DATA_IN 0x22 /* BASE+22, B, R */
#define SCAM_CONTROL 0x24 /* BASE+24, B, W */
#define SCAM_STATUS 0x24 /* BASE+24, B, R */
# define SCAM_MSG BIT(0)
# define SCAM_IO BIT(1)
# define SCAM_CD BIT(2)
# define SCAM_BSY BIT(3)
# define SCAM_SEL BIT(4)
# define SCAM_XFEROK BIT(5)
#define SCAM_DATA 0x26 /* BASE+26, B, R/W */
# define SD0 BIT(0)
# define SD1 BIT(1)
# define SD2 BIT(2)
# define SD3 BIT(3)
# define SD4 BIT(4)
# define SD5 BIT(5)
# define SD6 BIT(6)
# define SD7 BIT(7)
#define SACK_CNT 0x28 /* BASE+28, DW, R/W */
#define SREQ_CNT 0x2c /* BASE+2c, DW, R/W */
#define FIFO_DATA_LOW 0x30 /* BASE+30, B/W/DW, R/W */
#define FIFO_DATA_HIGH 0x32 /* BASE+32, B/W, R/W */
#define BM_START_ADR 0x34 /* BASE+34, DW, R/W */
#define BM_CNT 0x38 /* BASE+38, DW, R/W */
# define BM_COUNT_MASK 0x0001ffff
# define SGTEND BIT(31)
#define SGT_ADR 0x3c /* BASE+3c, DW, R/W */
#define WAIT_REG 0x40 /* Bi only */
#define SCSI_EXECUTE_PHASE 0x40 /* BASE+40, W, R */
# define COMMAND_PHASE BIT(0)
# define DATA_IN_PHASE BIT(1)
# define DATA_OUT_PHASE BIT(2)
# define MSGOUT_PHASE BIT(3)
# define STATUS_PHASE BIT(4)
# define ILLEGAL_PHASE BIT(5)
# define BUS_FREE_OCCUER BIT(6)
# define MSG_IN_OCCUER BIT(7)
# define MSG_OUT_OCCUER BIT(8)
# define SELECTION_TIMEOUT BIT(9)
# define MSGIN_00_VALID BIT(10)
# define MSGIN_02_VALID BIT(11)
# define MSGIN_03_VALID BIT(12)
# define MSGIN_04_VALID BIT(13)
# define AUTOSCSI_BUSY BIT(15)
#define SCSI_CSB_IN 0x42 /* BASE+42, B, R */
#define SCSI_MSG_OUT 0x44 /* BASE+44, DW, R/W */
# define MSGOUT_COUNT_MASK (BIT(0)|BIT(1))
# define MV_VALID BIT(7)
#define SEL_TIME_OUT 0x48 /* BASE+48, W, R/W */
#define SAVED_SACK_CNT 0x4c /* BASE+4c, DW, R */
#define HTOSDATADELAY 0x50 /* BASE+50, B, R/W */
#define STOHDATADELAY 0x54 /* BASE+54, B, R/W */
#define ACKSUMCHECKRD 0x58 /* BASE+58, W, R */
#define REQSUMCHECKRD 0x5c /* BASE+5c, W, R */
/********************/
/* indexed register */
/********************/
#define CLOCK_DIV 0x00 /* BASE+08, IDX+00, B, R/W */
# define CLOCK_2 BIT(0) /* MCLK/2 */
# define CLOCK_4 BIT(1) /* MCLK/4 */
# define PCICLK BIT(7) /* PCICLK (33MHz) */
#define TERM_PWR_CONTROL 0x01 /* BASE+08, IDX+01, B, R/W */
# define BPWR BIT(0)
# define SENSE BIT(1) /* Read Only */
#define EXT_PORT_DDR 0x02 /* BASE+08, IDX+02, B, R/W */
#define EXT_PORT 0x03 /* BASE+08, IDX+03, B, R/W */
# define LED_ON 0
# define LED_OFF 1
#define IRQ_SELECT 0x04 /* BASE+08, IDX+04, W, R/W */
# define IRQSELECT_RESELECT_IRQ BIT(0)
# define IRQSELECT_PHASE_CHANGE_IRQ BIT(1)
# define IRQSELECT_SCSIRESET_IRQ BIT(2)
# define IRQSELECT_TIMER_IRQ BIT(3)
# define IRQSELECT_FIFO_SHLD_IRQ BIT(4)
# define IRQSELECT_TARGET_ABORT_IRQ BIT(5)
# define IRQSELECT_MASTER_ABORT_IRQ BIT(6)
# define IRQSELECT_SERR_IRQ BIT(7)
# define IRQSELECT_PERR_IRQ BIT(8)
# define IRQSELECT_BMCNTERR_IRQ BIT(9)
# define IRQSELECT_AUTO_SCSI_SEQ_IRQ BIT(10)
#define OLD_SCSI_PHASE 0x05 /* BASE+08, IDX+05, B, R */
# define OLD_MSG BIT(0)
# define OLD_IO BIT(1)
# define OLD_CD BIT(2)
# define OLD_BUSY BIT(3)
#define FIFO_FULL_SHLD_COUNT 0x06 /* BASE+08, IDX+06, B, R/W */
#define FIFO_EMPTY_SHLD_COUNT 0x07 /* BASE+08, IDX+07, B, R/W */
#define EXP_ROM_CONTROL 0x08 /* BASE+08, IDX+08, B, R/W */
#define EXP_ROM_ADRL 0x09 /* BASE+08, IDX+09, W, R/W */
#define EXP_ROM_DATA 0x0a /* BASE+08, IDX+0a, B, R/W */
#define CHIP_MODE 0x0b /* Bi only */
# define OEM0 BIT(1)
# define OEM1 BIT(2)
# define OPTB BIT(3)
# define OPTC BIT(4)
# define OPTD BIT(5)
# define OPTE BIT(6)
# define OPTF BIT(7)
#define MISC_WR 0x0c /* BASE+08, IDX+0c, W, R/W */
#define MISC_RD 0x0c
# define SCSI_DIRECTION_DETECTOR_SELECT BIT(0)
# define SCSI2_HOST_DIRECTION_VALID BIT(1) /* Read only */
# define HOST2_SCSI_DIRECTION_VALID BIT(2) /* Read only */
# define DELAYED_BMSTART BIT(3)
# define MASTER_TERMINATION_SELECT BIT(4)
# define BMREQ_NEGATE_TIMING_SEL BIT(5)
# define AUTOSEL_TIMING_SEL BIT(6)
# define MISC_MABORT_MASK BIT(7)
# define BMSTOP_CHANGE2_NONDATA_PHASE BIT(8)
#define BM_CYCLE 0x0d /* BASE+08, IDX+0d, B, R/W */
# define BM_CYCLE0 BIT(0)
# define BM_CYCLE1 BIT(1)
# define BM_FRAME_ASSERT_TIMING BIT(2)
# define BM_IRDY_ASSERT_TIMING BIT(3)
# define BM_SINGLE_BUS_MASTER BIT(4)
# define MEMRD_CMD0 BIT(5)
# define SGT_AUTO_PARA_MEMED_CMD BIT(6)
# define MEMRD_CMD1 BIT(7)
#define SREQ_EDGH 0x0e /* BASE+08, IDX+0e, B, W */
# define SREQ_EDGH_SELECT BIT(0)
#define UP_CNT 0x0f /* BASE+08, IDX+0f, B, W */
#define CFG_CMD_STR 0x10 /* BASE+08, IDX+10, W, R */
#define CFG_LATE_CACHE 0x11 /* BASE+08, IDX+11, W, R/W */
#define CFG_BASE_ADR_1 0x12 /* BASE+08, IDX+12, W, R */
#define CFG_BASE_ADR_2 0x13 /* BASE+08, IDX+13, W, R */
#define CFG_INLINE 0x14 /* BASE+08, IDX+14, W, R */
#define SERIAL_ROM_CTL 0x15 /* BASE+08, IDX+15, B, R */
# define SCL BIT(0)
# define ENA BIT(1)
# define SDA BIT(2)
#define FIFO_HST_POINTER 0x16 /* BASE+08, IDX+16, B, R/W */
#define SREQ_DELAY 0x17 /* BASE+08, IDX+17, B, R/W */
#define SACK_DELAY 0x18 /* BASE+08, IDX+18, B, R/W */
#define SREQ_NOISE_CANCEL 0x19 /* BASE+08, IDX+19, B, R/W */
#define SDP_NOISE_CANCEL 0x1a /* BASE+08, IDX+1a, B, R/W */
#define DELAY_TEST 0x1b /* BASE+08, IDX+1b, B, R/W */
#define SD0_NOISE_CANCEL 0x20 /* BASE+08, IDX+20, B, R/W */
#define SD1_NOISE_CANCEL 0x21 /* BASE+08, IDX+21, B, R/W */
#define SD2_NOISE_CANCEL 0x22 /* BASE+08, IDX+22, B, R/W */
#define SD3_NOISE_CANCEL 0x23 /* BASE+08, IDX+23, B, R/W */
#define SD4_NOISE_CANCEL 0x24 /* BASE+08, IDX+24, B, R/W */
#define SD5_NOISE_CANCEL 0x25 /* BASE+08, IDX+25, B, R/W */
#define SD6_NOISE_CANCEL 0x26 /* BASE+08, IDX+26, B, R/W */
#define SD7_NOISE_CANCEL 0x27 /* BASE+08, IDX+27, B, R/W */
/*
* Useful Bus Monitor status combinations.
*/
#define BUSMON_BUS_FREE 0
#define BUSMON_COMMAND ( BUSMON_BSY | BUSMON_CD | BUSMON_REQ )
#define BUSMON_MESSAGE_IN ( BUSMON_BSY | BUSMON_MSG | BUSMON_IO | BUSMON_CD | BUSMON_REQ )
#define BUSMON_MESSAGE_OUT ( BUSMON_BSY | BUSMON_MSG | BUSMON_CD | BUSMON_REQ )
#define BUSMON_DATA_IN ( BUSMON_BSY | BUSMON_IO | BUSMON_REQ )
#define BUSMON_DATA_OUT ( BUSMON_BSY | BUSMON_REQ )
#define BUSMON_STATUS ( BUSMON_BSY | BUSMON_IO | BUSMON_CD | BUSMON_REQ )
#define BUSMON_RESELECT ( BUSMON_IO | BUSMON_SEL)
#define BUSMON_PHASE_MASK ( BUSMON_MSG | BUSMON_IO | BUSMON_CD | BUSMON_SEL)
#define BUSPHASE_COMMAND ( BUSMON_COMMAND & BUSMON_PHASE_MASK )
#define BUSPHASE_MESSAGE_IN ( BUSMON_MESSAGE_IN & BUSMON_PHASE_MASK )
#define BUSPHASE_MESSAGE_OUT ( BUSMON_MESSAGE_OUT & BUSMON_PHASE_MASK )
#define BUSPHASE_DATA_IN ( BUSMON_DATA_IN & BUSMON_PHASE_MASK )
#define BUSPHASE_DATA_OUT ( BUSMON_DATA_OUT & BUSMON_PHASE_MASK )
#define BUSPHASE_STATUS ( BUSMON_STATUS & BUSMON_PHASE_MASK )
#define BUSPHASE_SELECT ( BUSMON_SEL | BUSMON_IO )
#endif _NSP32_H
/* end */
/*
* Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus SCSI Host Bus Adapter driver
* Debug routine
*
* This software may be used and distributed according to the terms of
* the GNU General Public License.
*/
/*
* Show the command data of a command
*/
static const char unknown[] = "UNKNOWN";
static const char * group_0_commands[] = {
/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic",
/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
};
static const char *group_1_commands[] = {
/* 20-22 */ unknown, unknown, unknown,
/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)",
/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown,
/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position",
/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
/* 38-3c */ "Medium Scan", "Compare","Copy Verify", "Write Buffer", "Read Buffer",
/* 3d-3f */ "Update Block", "Read Long", "Write Long",
};
static const char *group_2_commands[] = {
/* 40-41 */ "Change Definition", "Write Same",
/* 42-48 */ "Read Sub-Ch(cd)", "Read TOC", "Read Header(cd)", "Play Audio(cd)", unknown, "Play Audio MSF(cd)", "Play Audio Track/Index(cd)",
/* 49-4f */ "Play Track Relative(10)(cd)", unknown, "Pause/Resume(cd)", "Log Select", "Log Sense", unknown, unknown,
/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
/* 5c-5f */ unknown, unknown, unknown,
};
#define group(opcode) (((opcode) >> 5) & 7)
#define RESERVED_GROUP 0
#define VENDOR_GROUP 1
#define NOTEXT_GROUP 2
static const char **commands[] = {
group_0_commands, group_1_commands, group_2_commands,
(const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP,
(const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP,
(const char **) VENDOR_GROUP
};
static const char reserved[] = "RESERVED";
static const char vendor[] = "VENDOR SPECIFIC";
static void print_opcodek(unsigned char opcode)
{
const char **table = commands[ group(opcode) ];
switch ((unsigned long) table) {
case RESERVED_GROUP:
printk("%s[%02x] ", reserved, opcode);
break;
case NOTEXT_GROUP:
printk("%s(notext)[%02x] ", unknown, opcode);
break;
case VENDOR_GROUP:
printk("%s[%02x] ", vendor, opcode);
break;
default:
if (table[opcode & 0x1f] != unknown)
printk("%s[%02x] ", table[opcode & 0x1f], opcode);
else
printk("%s[%02x] ", unknown, opcode);
break;
}
}
static void print_commandk (unsigned char *command)
{
int i,s;
// printk(KERN_DEBUG);
print_opcodek(command[0]);
/*printk(KERN_DEBUG __func__ " ");*/
if ((command[0] >> 5) == 6 ||
(command[0] >> 5) == 7 ) {
s = 12; /* vender specific */
} else {
s = COMMAND_SIZE(command[0]);
}
for ( i = 1; i < s; ++i) {
printk("%02x ", command[i]);
}
switch (s) {
case 6:
printk("LBA=%d len=%d",
(((unsigned int)command[1] & 0x0f) << 16) |
( (unsigned int)command[2] << 8) |
( (unsigned int)command[3] ),
(unsigned int)command[4]
);
break;
case 10:
printk("LBA=%d len=%d",
((unsigned int)command[2] << 24) |
((unsigned int)command[3] << 16) |
((unsigned int)command[4] << 8) |
((unsigned int)command[5] ),
((unsigned int)command[7] << 8) |
((unsigned int)command[8] )
);
break;
case 12:
printk("LBA=%d len=%d",
((unsigned int)command[2] << 24) |
((unsigned int)command[3] << 16) |
((unsigned int)command[4] << 8) |
((unsigned int)command[5] ),
((unsigned int)command[6] << 24) |
((unsigned int)command[7] << 16) |
((unsigned int)command[8] << 8) |
((unsigned int)command[9] )
);
break;
default:
break;
}
printk("\n");
}
static void show_command(Scsi_Cmnd *ptr)
{
print_commandk(ptr->cmnd);
}
static void show_busphase(unsigned char stat)
{
switch(stat) {
case BUSPHASE_COMMAND:
printk( "BUSPHASE_COMMAND\n");
break;
case BUSPHASE_MESSAGE_IN:
printk( "BUSPHASE_MESSAGE_IN\n");
break;
case BUSPHASE_MESSAGE_OUT:
printk( "BUSPHASE_MESSAGE_OUT\n");
break;
case BUSPHASE_DATA_IN:
printk( "BUSPHASE_DATA_IN\n");
break;
case BUSPHASE_DATA_OUT:
printk( "BUSPHASE_DATA_OUT\n");
break;
case BUSPHASE_STATUS:
printk( "BUSPHASE_STATUS\n");
break;
case BUSPHASE_SELECT:
printk( "BUSPHASE_SELECT\n");
break;
default:
printk( "BUSPHASE_other: 0x%x\n", stat);
break;
}
}
static void show_autophase(unsigned short i)
{
printk("auto: 0x%x,", i);
if(i & COMMAND_PHASE) {
printk(" cmd");
}
if(i & DATA_IN_PHASE) {
printk(" din");
}
if(i & DATA_OUT_PHASE) {
printk(" dout");
}
if(i & MSGOUT_PHASE) {
printk(" mout");
}
if(i & STATUS_PHASE) {
printk(" stat");
}
if(i & ILLEGAL_PHASE) {
printk(" ill");
}
if(i & BUS_FREE_OCCUER) {
printk(" bfree-o");
}
if(i & MSG_IN_OCCUER) {
printk(" min-o");
}
if(i & MSG_OUT_OCCUER) {
printk(" mout-o");
}
if(i & SELECTION_TIMEOUT) {
printk(" sel");
}
if(i & MSGIN_00_VALID) {
printk(" m0");
}
if(i & MSGIN_02_VALID) {
printk(" m2");
}
if(i & MSGIN_03_VALID) {
printk(" m3");
}
if(i & MSGIN_04_VALID) {
printk(" m4");
}
if(i & AUTOSCSI_BUSY) {
printk(" busy");
}
printk("\n");
}
static void nsp32_print_register(int base)
{
if (!(NSP32_DEBUG_MASK & NSP32_SPECIAL_PRINT_REGISTER))
return;
printk("Phase=0x%x, ", nsp32_read1(base, SCSI_BUS_MONITOR));
printk("OldPhase=0x%x, ", nsp32_index_read1(base, OLD_SCSI_PHASE));
printk("syncreg=0x%x, ", nsp32_read1(base, SYNC_REG));
printk("ackwidth=0x%x, ", nsp32_read1(base, ACK_WIDTH));
printk("sgtpaddr=0x%lx, ", nsp32_read4(base, SGT_ADR));
printk("scsioutlatch=0x%x, ", nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
printk("msgout=0x%lx, ", nsp32_read4(base, SCSI_MSG_OUT));
printk("miscrd=0x%x, ", nsp32_index_read2(base, MISC_WR));
printk("seltimeout=0x%x, ", nsp32_read2(base, SEL_TIME_OUT));
printk("sreqrate=0x%x, ", nsp32_read1(base, SREQ_SMPL_RATE));
printk("transStatus=0x%x, ", nsp32_read2(base, TRANSFER_STATUS));
printk("reselectid=0x%x, ", nsp32_read2(base, COMMAND_CONTROL));
printk("arbit=0x%x, ", nsp32_read1(base, ARBIT_STATUS));
printk("BmStart=0x%lx, ", nsp32_read4(base, BM_START_ADR));
printk("BmCount=0x%lx, ", nsp32_read4(base, BM_CNT));
printk("SackCnt=0x%lx, ", nsp32_read4(base, SACK_CNT));
printk("SReqCnt=0x%lx, ", nsp32_read4(base, SREQ_CNT));
printk("SavedSackCnt=0x%lx, ", nsp32_read4(base, SAVED_SACK_CNT));
printk("ScsiBusControl=0x%x, ", nsp32_read1(base, SCSI_BUS_CONTROL));
printk("FifoRestCnt=0x%x, ", nsp32_read2(base, FIFO_REST_CNT));
printk("CdbIn=0x%x, ", nsp32_read1(base, SCSI_CSB_IN));
printk("\n");
if (0) {
printk("execph=0x%x, ", nsp32_read2(base, SCSI_EXECUTE_PHASE));
printk("IrqStatus=0x%x, ", nsp32_read2(base, IRQ_STATUS));
printk("\n");
}
}
/*
* Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus SCSI Host Bus Adapter driver
* I/O routine
*
* This software may be used and distributed according to the terms of
* the GNU General Public License.
*/
#ifndef _NSP32_IO_H
#define _NSP32_IO_H
static inline void nsp32_write1(unsigned int base,
unsigned int index,
unsigned char val)
{
outb(val, (base + index));
}
static inline unsigned char nsp32_read1(unsigned int base,
unsigned int index)
{
return inb(base + index);
}
static inline void nsp32_write2(unsigned int base,
unsigned int index,
unsigned short val)
{
outw(cpu_to_le16(val), (base + index));
}
static inline unsigned short nsp32_read2(unsigned int base,
unsigned int index)
{
return le16_to_cpu(inw(base + index));
}
static inline void nsp32_write4(unsigned int base,
unsigned int index,
unsigned long val)
{
outl(cpu_to_le32(val), (base + index));
}
static inline unsigned long nsp32_read4(unsigned int base,
unsigned int index)
{
return le32_to_cpu(inl(base + index));
}
/*==============================================*/
static inline void nsp32_mmio_write1(unsigned long base,
unsigned int index,
unsigned char val)
{
volatile unsigned char *ptr;
ptr = (unsigned char *)(base + NSP32_MMIO_OFFSET + index);
*ptr = val;
}
static inline unsigned char nsp32_mmio_read1(unsigned long base,
unsigned int index)
{
volatile unsigned char *ptr;
ptr = (unsigned char *)(base + NSP32_MMIO_OFFSET + index);
return *ptr;
}
static inline void nsp32_mmio_write2(unsigned long base,
unsigned int index,
unsigned short val)
{
volatile unsigned short *ptr;
ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + index);
*ptr = cpu_to_le16(val);
}
static inline unsigned short nsp32_mmio_read2(unsigned long base,
unsigned int index)
{
volatile unsigned short *ptr;
//printk(__FUNCTION__ "\n");
ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + index);
return le16_to_cpu(*ptr);
}
static inline void nsp32_mmio_write4(unsigned long base,
unsigned int index,
unsigned long val)
{
volatile unsigned long *ptr;
ptr = (unsigned long *)(base + NSP32_MMIO_OFFSET + index);
*ptr = cpu_to_le32(val);
}
static inline unsigned long nsp32_mmio_read4(unsigned long base,
unsigned int index)
{
volatile unsigned long *ptr;
//printk(__FUNCTION__ "\n");
ptr = (unsigned long *)(base + NSP32_MMIO_OFFSET + index);
return le32_to_cpu(*ptr);
}
/*=============================================*/
static inline unsigned char nsp32_index_read1(unsigned int base,
unsigned int reg)
{
outb(reg, base + INDEX_REG);
return inb(base + DATA_REG_LOW);
}
static inline void nsp32_index_write1(unsigned int base,
unsigned int reg,
unsigned char val)
{
outb(reg, base + INDEX_REG);
outb(val, base + DATA_REG_LOW);
}
static inline unsigned short nsp32_index_read2(unsigned int base,
unsigned int reg)
{
outb(reg, base + INDEX_REG);
return le16_to_cpu(inw(base + DATA_REG_LOW));
}
static inline void nsp32_index_write2(unsigned int base,
unsigned int reg,
unsigned short val)
{
outb(reg, base + INDEX_REG);
outw(cpu_to_le16(val), base + DATA_REG_LOW);
}
static inline unsigned long nsp32_index_read4(unsigned int base,
unsigned int reg)
{
unsigned long h,l;
outb(reg, base + INDEX_REG);
l = le16_to_cpu(inw(base + DATA_REG_LOW));
h = le16_to_cpu(inw(base + DATA_REG_HI ));
return ((h << 16) | l);
}
static inline void nsp32_index_write4(unsigned int base,
unsigned int reg,
unsigned long val)
{
unsigned long h,l;
h = (val & 0xffff0000) >> 16;
l = (val & 0x0000ffff) >> 0;
outb(reg, base + INDEX_REG);
outw(cpu_to_le16(l), base + DATA_REG_LOW);
outw(cpu_to_le16(h), base + DATA_REG_HI);
}
/* ===================================*/
static inline unsigned char nsp32_mmio_index_read1(unsigned int base,
unsigned int reg)
{
volatile unsigned short *index_ptr, *data_ptr;
index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
data_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
*index_ptr = reg;
return ((*data_ptr) & 0xff);
}
static inline void nsp32_mmio_index_write1(unsigned int base,
unsigned int reg,
unsigned char val)
{
volatile unsigned short *index_ptr, *data_ptr;
index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
data_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
*index_ptr = reg;
*data_ptr = (unsigned short)val;
}
static inline unsigned short nsp32_mmio_index_read2(unsigned int base,
unsigned int reg)
{
volatile unsigned short *index_ptr, *data_ptr;
index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
data_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
*index_ptr = reg;
return le16_to_cpu(*data_ptr);
}
static inline void nsp32_mmio_index_write2(unsigned int base,
unsigned int reg,
unsigned short val)
{
volatile unsigned short *index_ptr, *data_ptr;
index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
data_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
*index_ptr = reg;
*data_ptr = val;
}
/*-------------------------------------------------------------------*/
static inline void nsp32_multi_read4(unsigned int BaseAddr,
unsigned int Register,
void *buf,
unsigned long count)
{
insl(BaseAddr + Register, buf, count);
}
static inline void nsp32_fifo_read(unsigned int base,
void *buf,
unsigned long count)
{
//DEBUG(0, __FUNCTION__ "() buf=0x%p, count=0x%lx*4\n", buf, count);
nsp32_multi_read4(base, FIFO_DATA_LOW, buf, count);
}
static inline void nsp32_multi_write4(unsigned int BaseAddr,
unsigned int Register,
void *buf,
unsigned long count)
{
outsl(BaseAddr + Register, buf, count);
}
static inline void nsp32_fifo_write(unsigned int base,
void *buf,
unsigned long count)
{
nsp32_multi_write4(base, FIFO_DATA_LOW, buf, count);
}
#endif _NSP32_IO_H
/* end */
/* include/version.h. Generated automatically by configure. */
#define CONFIG_SND_VERSION "0.9.0rc3"
#define CONFIG_SND_DATE " (Tue Oct 01 14:40:23 2002 UTC)"
#define CONFIG_SND_DATE " (Fri Oct 04 13:09:13 2002 UTC)"
......@@ -10,8 +10,8 @@ CONFIG_BLUEZ
BlueZ Core (HCI device and connection manager, scheduler)
HCI Device drivers (interface to the hardware)
L2CAP Module (L2CAP protocol)
RFCOMM Module (RFCOMM protocol)
SCO Module (SCO links)
RFCOMM Module (RFCOMM protocol)
BNEP Module (BNEP protocol)
Say Y here to enable Linux Bluetooth support and to build BlueZ Core
......@@ -33,19 +33,6 @@ CONFIG_BLUEZ_L2CAP
Say Y here to compile L2CAP support into the kernel or say M to
compile it as module (l2cap.o).
RFCOMM protocol support
CONFIG_BLUEZ_RFCOMM
RFCOMM provides connection oriented stream transport. RFCOMM
support is required for Dialup Networking, OBEX and other Bluetooth
applications.
Say Y here to compile RFCOMM support into the kernel or say M to
compile it as module (rfcomm.o).
RFCOMM TTY emulation support
CONFIG_RFCOMM_TTY
This options enables TTY emulation support for RFCOMM channels.
SCO links support
CONFIG_BLUEZ_SCO
SCO link provides voice transport over Bluetooth. SCO support is
......@@ -54,16 +41,3 @@ CONFIG_BLUEZ_SCO
Say Y here to compile SCO support into the kernel or say M to
compile it as module (sco.o).
BNEP protocol support
CONFIG_BLUEZ_BNEP
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
emulation layer on top of Bluetooth. BNEP is required for Bluetooth
PAN (Personal Area Network).
To use BNEP, you will need user-space utilities provided in the
BlueZ-PAN package.
For more information, see <http://bluez.sourceforge.net>.
Say Y here to compile BNEP support into the kernel or say M to
compile it as module (bnep.o).
#
# Bluetooth configuration
# Bluetooth subsystem configuration
#
if [ "$CONFIG_NET" != "n" ]; then
mainmenu_option next_comment
comment 'Bluetooth support'
dep_tristate 'Bluetooth subsystem support' CONFIG_BLUEZ $CONFIG_NET
......@@ -14,6 +15,7 @@ if [ "$CONFIG_NET" != "n" ]; then
source net/bluetooth/bnep/Config.in
source drivers/bluetooth/Config.in
fi
endmenu
fi
#
# Makefile for the Bluetooth subsystem
# Makefile for the Linux Bluetooth subsystem.
#
export-objs := syms.o
export-objs := syms.o
obj-$(CONFIG_BLUEZ) += bluez.o
obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
obj-$(CONFIG_BLUEZ_SCO) += sco.o
obj-$(CONFIG_BLUEZ) += bluez.o
obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
obj-$(CONFIG_BLUEZ_SCO) += sco.o
obj-$(CONFIG_BLUEZ_RFCOMM) += rfcomm/
obj-$(CONFIG_BLUEZ_BNEP) += bnep/
subdir-$(CONFIG_BLUEZ_BNEP) += bnep
ifeq ($(CONFIG_BLUEZ_BNEP),y)
obj-y += bnep/bnep.o
endif
subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
obj-y += rfcomm/rfcomm.o
endif
bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
include $(TOPDIR)/Rules.make
BNEP protocol support
CONFIG_BLUEZ_BNEP
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
emulation layer on top of Bluetooth. BNEP is required for Bluetooth
PAN (Personal Area Network).
To use BNEP, you will need user-space utilities provided in the
BlueZ-PAN package.
For more information, see <http://bluez.sourceforge.net>.
Say Y here to compile BNEP support into the kernel or say M to
compile it as module (bnep.o).
BNEP multicast filter support
CONFIG_BLUEZ_BNEP_MC_FILTER
This option enables the multicast filter support for BNEP.
BNEP protocol filter support
CONFIG_BLUEZ_BNEP_PROTO_FILTER
This option enables the protocol filter support for BNEP.
dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP
if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then
bool ' Multicast filter support' CONFIG_BNEP_MC_FILTER
bool ' Protocol filter support' CONFIG_BNEP_PROTO_FILTER
bool ' Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER
bool ' Protocol filter support' CONFIG_BLUEZ_BNEP_PROTO_FILTER
fi
#
# Makefile for BNEP protocol
# Makefile for the Linux Bluetooth BNEP layer.
#
O_TARGET := bnep.o
obj-$(CONFIG_BLUEZ_BNEP) += bnep.o
obj-y := core.o sock.o netdev.o crc32.o
obj-m += $(O_TARGET)
bnep-objs := core.o sock.o netdev.o crc32.o
include $(TOPDIR)/Rules.make
......@@ -58,7 +58,7 @@
#include "bnep.h"
#ifndef CONFIG_BNEP_DEBUG
#ifndef CONFIG_BLUEZ_BNEP_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
......@@ -129,7 +129,7 @@ static int bnep_ctrl_set_netfilter(struct bnep_session *s, struct sk_buff *skb)
BT_DBG("filter len %d", n);
#ifdef CONFIG_BNEP_PROTO_FILTER
#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
n /= 4;
if (n <= BNEP_MAX_PROTO_FILTERS) {
struct bnep_proto_filter *f = s->proto_filter;
......@@ -171,7 +171,7 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, struct sk_buff *skb)
BT_DBG("filter len %d", n);
#ifdef CONFIG_BNEP_MC_FILTER
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
n /= (ETH_ALEN * 2);
if (n > 0) {
......@@ -545,12 +545,12 @@ int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock)
s->msg.msg_flags = MSG_NOSIGNAL;
#ifdef CONFIG_BNEP_MC_FILTER
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
/* Set default mc filter */
set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
#endif
#ifdef CONFIG_BNEP_PROTO_FILTER
#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
/* Set default protocol filter */
/* (IPv4, ARP) */
......
......@@ -46,7 +46,7 @@
#include "bnep.h"
#ifndef CONFIG_BNEP_DEBUG
#ifndef CONFIG_BLUEZ_BNEP_DEBUG
#undef BT_DBG
#define BT_DBG( A... )
#endif
......@@ -73,7 +73,7 @@ static struct net_device_stats *bnep_net_get_stats(struct net_device *dev)
static void bnep_net_set_mc_list(struct net_device *dev)
{
#ifdef CONFIG_BNEP_MC_FILTER
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
struct bnep_session *s = dev->priv;
struct sock *sk = s->sock->sk;
struct bnep_set_filter_req *r;
......@@ -143,7 +143,7 @@ static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EINVAL;
}
#ifdef CONFIG_BNEP_MC_FILTER
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s)
{
struct ethhdr *eh = (void *) skb->data;
......@@ -154,7 +154,7 @@ static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s
}
#endif
#ifdef CONFIG_BNEP_PROTO_FILTER
#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
/* Determine ether protocol. Based on eth_type_trans. */
static inline u16 bnep_net_eth_proto(struct sk_buff *skb)
{
......@@ -192,14 +192,14 @@ static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
BT_DBG("skb %p, dev %p", skb, dev);
#ifdef CONFIG_BNEP_MC_FILTER
#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
if (bnep_net_mc_filter(skb, s)) {
kfree_skb(skb);
return 0;
}
#endif
#ifdef CONFIG_BNEP_PROTO_FILTER
#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
if (bnep_net_proto_filter(skb, s)) {
kfree_skb(skb);
return 0;
......
......@@ -50,7 +50,7 @@
#include "bnep.h"
#ifndef CONFIG_BNEP_DEBUG
#ifndef CONFIG_BLUEZ_BNEP_DEBUG
#undef BT_DBG
#define BT_DBG( A... )
#endif
......
RFCOMM protocol support
CONFIG_BLUEZ_RFCOMM
RFCOMM provides connection oriented stream transport. RFCOMM
support is required for Dialup Networking, OBEX and other Bluetooth
applications.
Say Y here to compile RFCOMM support into the kernel or say M to
compile it as module (rfcomm.o).
RFCOMM TTY emulation support
CONFIG_BLUEZ_RFCOMM_TTY
This option enables TTY emulation support for RFCOMM channels.
dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP
if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then
bool ' RFCOMM TTY support' CONFIG_RFCOMM_TTY
bool ' RFCOMM TTY support' CONFIG_BLUEZ_RFCOMM_TTY
fi
#
# Makefile for BNEP protocol
# Makefile for the Linux Bluetooth RFCOMM layer.
#
O_TARGET := rfcomm.o
obj-$(CONFIG_BLUEZ_RFCOMM) += rfcomm.o
obj-y := core.o sock.o crc.o
obj-$(CONFIG_RFCOMM_TTY) += tty.o
obj-m += $(O_TARGET)
rfcomm-y := core.o sock.o crc.o
rfcomm-$(CONFIG_BLUEZ_RFCOMM_TTY) += tty.o
rfcomm-objs := $(rfcomm-y)
include $(TOPDIR)/Rules.make
......@@ -52,7 +52,7 @@
#define VERSION "0.3"
#ifndef CONFIG_RFCOMM_DEBUG
#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
......@@ -1679,7 +1679,10 @@ int __init rfcomm_init(void)
kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
rfcomm_init_sockets();
#ifdef CONFIG_BLUEZ_RFCOMM_TTY
rfcomm_init_ttys();
#endif
BT_INFO("BlueZ RFCOMM ver %s", VERSION);
BT_INFO("Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>");
......@@ -1698,7 +1701,10 @@ void rfcomm_cleanup(void)
while (atomic_read(&running))
schedule();
#ifdef CONFIG_BLUEZ_RFCOMM_TTY
rfcomm_cleanup_ttys();
#endif
rfcomm_cleanup_sockets();
return;
}
......
......@@ -53,7 +53,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/rfcomm.h>
#ifndef CONFIG_RFCOMM_DEBUG
#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
......@@ -704,7 +704,7 @@ static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned lon
lock_sock(sk);
#ifdef CONFIG_RFCOMM_TTY
#ifdef CONFIG_BLUEZ_RFCOMM_TTY
err = rfcomm_dev_ioctl(sk, cmd, arg);
#else
err = -EOPNOTSUPP;
......
......@@ -40,7 +40,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/rfcomm.h>
#ifndef CONFIG_RFCOMM_DEBUG
#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
......
......@@ -960,7 +960,6 @@ void snd_info_free_device(snd_info_entry_t * entry)
{
#ifdef CONFIG_DEVFS_FS
char dname[32];
devfs_handle_t master;
#endif
snd_runtime_check(entry, return);
......@@ -970,12 +969,7 @@ void snd_info_free_device(snd_info_entry_t * entry)
#ifdef CONFIG_DEVFS_FS
if (entry->p && strncmp(entry->name, "controlC", 8)) {
sprintf(dname, "snd/%s", entry->name);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
master = devfs_find_handle(NULL, dname, strlen(dname), 0, 0, DEVFS_SPECIAL_CHR, 0);
devfs_unregister(master);
#else
devfs_find_and_unregister(NULL, dname, 0, 0, DEVFS_SPECIAL_CHR, 0);
#endif
}
#endif
snd_info_free_entry(entry);
......
......@@ -358,21 +358,12 @@ static int __init alsa_sound_init(void)
static void __exit alsa_sound_exit(void)
{
#ifdef CONFIG_DEVFS_FS
devfs_handle_t master;
char controlname[24];
short controlnum;
for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) {
sprintf(controlname, "snd/controlC%d", controlnum);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
master = devfs_find_handle(NULL, controlname, strlen(controlname), 0, 0, DEVFS_SPECIAL_CHR, 0);
devfs_unregister(master);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
master = devfs_find_handle(NULL, controlname, 0, 0, DEVFS_SPECIAL_CHR, 0);
devfs_unregister(master);
#else
devfs_find_and_unregister(NULL, controlname, 0, 0, DEVFS_SPECIAL_CHR, 0);
#endif
}
#endif
......
......@@ -7,7 +7,7 @@ dep_tristate 'ALi PCI Audio M5451' CONFIG_SND_ALI5451 $CONFIG_SND
dep_tristate 'Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x' CONFIG_SND_CS46XX $CONFIG_SND
dep_mbool ' Cirrus Logic (Sound Fusion) New DSP support (EXPERIMENTAL)' CONFIG_SND_CS46XX_NEW_DSP $CONFIG_SND_CS46XX $CONFIG_EXPERIMENTAL
dep_tristate 'Cirrus Logic (Sound Fusion) CS4281' CONFIG_SND_CS4281 $CONFIG_SND
dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND
dep_tristate 'EMU10K1 (SB Live! & Audigy, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND
dep_tristate 'Korg 1212 IO' CONFIG_SND_KORG1212 $CONFIG_SND
dep_tristate 'NeoMagic NM256AV/ZX' CONFIG_SND_NM256 $CONFIG_SND
dep_tristate 'RME Digi32, 32/8, 32 PRO' CONFIG_SND_RME32 $CONFIG_SND
......@@ -25,7 +25,7 @@ dep_tristate 'ESS ES1968/1978 (Maestro-1/2/2E)' CONFIG_SND_ES1968 $CONFIG_SND
dep_tristate 'ESS Allegro/Maestro3' CONFIG_SND_MAESTRO3 $CONFIG_SND
dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND
dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND
dep_tristate 'Intel i810/i820/i830/i840/MX440 integrated audio' CONFIG_SND_INTEL8X0 $CONFIG_SND
dep_tristate 'Intel i8x0/MX440, SiS 7012; Ali 5455; NForce Audio; AMD768/8111' CONFIG_SND_INTEL8X0 $CONFIG_SND
dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND
dep_tristate 'VIA 82C686A/B, 8233 South Bridge' CONFIG_SND_VIA82XX $CONFIG_SND
......
......@@ -1577,7 +1577,6 @@ int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip)
int cs46xx_dsp_enable_spdif_in (cs46xx_t *chip)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
unsigned int flags;
/* turn on amplifier */
chip->active_ctrl(chip, 1);
......
......@@ -1410,7 +1410,6 @@ int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
{
dsp_spos_instance_t * ins = chip->dsp_spos_instance;
dsp_scb_descriptor_t * parent_scb;
unsigned int flags;
snd_assert (src->parent_scb_ptr == NULL, return -EINVAL );
snd_assert(ins->master_mix_scb !=NULL, return -EINVAL );
......
......@@ -122,7 +122,7 @@ static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list,
* Remove a node from the chain. Called with the lock asserted
*/
static void __sound_remove_unit(struct sound_unit **list, int unit)
static struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit)
{
while(*list)
{
......@@ -130,13 +130,12 @@ static void __sound_remove_unit(struct sound_unit **list, int unit)
if(p->unit_minor==unit)
{
*list=p->next;
devfs_unregister (p->de);
kfree(p);
return;
return p;
}
list=&(p->next);
}
printk(KERN_ERR "Sound device %d went missing!\n", unit);
return NULL;
}
/*
......@@ -189,9 +188,15 @@ static int sound_insert_unit(struct sound_unit **list, struct file_operations *f
static void sound_remove_unit(struct sound_unit **list, int unit)
{
struct sound_unit *p;
spin_lock(&sound_loader_lock);
__sound_remove_unit(list, unit);
p = __sound_remove_unit(list, unit);
spin_unlock(&sound_loader_lock);
if (p) {
devfs_unregister (p->de);
kfree(p);
}
}
/*
......
......@@ -284,6 +284,16 @@ static int prepare_capture_urb(snd_usb_substream_t *subs,
urb->transfer_buffer = ctx->buf;
urb->transfer_buffer_length = offs;
urb->interval = 1;
#if 0 // for check
if (! urb->bandwidth) {
int bustime;
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0)
return bustime;
printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets);
usb_claim_bandwidth(urb->dev, urb, bustime, 1);
}
#endif // for check
return 0;
}
......@@ -305,8 +315,10 @@ static int retire_capture_urb(snd_usb_substream_t *subs,
for (i = 0; i < urb->number_of_packets; i++) {
cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (urb->iso_frame_desc[i].status) /* active? hmm, skip this */
continue;
if (urb->iso_frame_desc[i].status) {
snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
// continue;
}
len = urb->iso_frame_desc[i].actual_length / stride;
if (! len)
continue;
......@@ -1009,6 +1021,7 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
}
/* if endpoint has sampling rate control, set it */
if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) {
int crate;
data[0] = runtime->rate;
data[1] = runtime->rate >> 8;
data[2] = runtime->rate >> 16;
......@@ -1026,8 +1039,11 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
dev->devnum, subs->interface, fmt->altsetting, ep);
return err;
}
runtime->rate = data[0] | (data[1] << 8) | (data[2] << 16);
// printk("ok, getting back rate to %d\n", runtime->rate);
crate = data[0] | (data[1] << 8) | (data[2] << 16);
if (crate != runtime->rate) {
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, runtime->rate);
// runtime->rate = crate;
}
}
/* always fill max packet size */
if (fmt->attributes & EP_CS_ATTR_FILL_MAX)
......@@ -1292,14 +1308,14 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype
* entry point for linux usb interface
*/
#ifndef OLD_USB
#ifdef OLD_USB
static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id);
static void usb_audio_disconnect(struct usb_device *dev, void *ptr);
#else
static int usb_audio_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void usb_audio_disconnect(struct usb_interface *intf);
#else
static void * usb_audio_probe(usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id);
static void usb_audio_disconnect(struct usb_device *dev, void *ptr);
#endif
static struct usb_device_id usb_audio_ids [] = {
......@@ -1810,7 +1826,8 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, unsigned char *buffer, i
* parse audio control descriptor and create pcm/midi streams
*/
static int snd_usb_create_midi_interface(snd_usb_audio_t *chip, int ifnum,
static int snd_usb_create_midi_interface(snd_usb_audio_t *chip,
struct usb_interface *iface,
const snd_usb_audio_quirk_t *quirk);
static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
......@@ -1850,7 +1867,7 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
}
if (iface->altsetting[0].bInterfaceClass == USB_CLASS_AUDIO &&
iface->altsetting[0].bInterfaceSubClass == USB_SUBCLASS_MIDI_STREAMING) {
if (snd_usb_create_midi_interface(chip, j, NULL) < 0) {
if (snd_usb_create_midi_interface(chip, iface, NULL) < 0) {
snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", dev->devnum, ctrlif, j);
continue;
}
......@@ -1871,7 +1888,8 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
return 0;
}
static int snd_usb_create_midi_interface(snd_usb_audio_t *chip, int ifnum,
static int snd_usb_create_midi_interface(snd_usb_audio_t *chip,
struct usb_interface *iface,
const snd_usb_audio_quirk_t *quirk)
{
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
......@@ -1888,18 +1906,20 @@ static int snd_usb_create_midi_interface(snd_usb_audio_t *chip, int ifnum,
strcpy(seq_device->name, chip->card->shortname);
umidi = (snd_usb_midi_t *)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
umidi->chip = chip;
umidi->ifnum = ifnum;
umidi->iface = iface;
umidi->ifnum = iface->altsetting->bInterfaceNumber;
umidi->quirk = quirk;
umidi->seq_client = -1;
#endif
return 0;
}
static inline int snd_usb_create_quirk(snd_usb_audio_t *chip, int ifnum,
static inline int snd_usb_create_quirk(snd_usb_audio_t *chip,
struct usb_interface *iface,
const snd_usb_audio_quirk_t *quirk)
{
/* in the future, there may be quirks for PCM devices */
return snd_usb_create_midi_interface(chip, ifnum, quirk);
return snd_usb_create_midi_interface(chip, iface, quirk);
}
......@@ -2050,27 +2070,18 @@ static int alloc_desc_buffer(struct usb_device *dev, int index, unsigned char **
* only at the first time. the successive calls of this function will
* append the pcm interface to the corresponding card.
*/
#ifndef OLD_USB
static int usb_audio_probe(struct usb_interface *intf,
const struct usb_device_id *id)
#else
static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
#endif
static void *snd_usb_audio_probe(struct usb_device *dev,
struct usb_interface *intf,
const struct usb_device_id *id)
{
#ifndef OLD_USB
struct usb_device *dev = interface_to_usbdev(intf);
int ifnum = intf->altsetting->bInterfaceNumber;
#endif
struct usb_config_descriptor *config = dev->actconfig;
const snd_usb_audio_quirk_t *quirk = (const snd_usb_audio_quirk_t *)id->driver_info;
unsigned char *buffer;
unsigned int index;
int i, buflen;
int i;
snd_card_t *card;
snd_usb_audio_t *chip;
int ifnum = intf->altsetting->bInterfaceNumber;
if (quirk && ifnum != quirk->ifnum)
if (quirk && quirk->ifnum != QUIRK_ANY_INTERFACE && ifnum != quirk->ifnum)
goto __err_val;
if (usb_set_configuration(dev, config->bConfigurationValue) < 0) {
......@@ -2078,11 +2089,6 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
goto __err_val;
}
index = dev->actconfig - config;
buflen = alloc_desc_buffer(dev, index, &buffer);
if (buflen <= 0)
goto __err_val;
/*
* found a config. now register to ALSA
*/
......@@ -2124,12 +2130,24 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
}
if (!quirk) {
if (snd_usb_create_streams(chip, ifnum, buffer, buflen) < 0)
/* USB audio interface */
unsigned char *buffer;
unsigned int index;
int buflen;
index = dev->actconfig - config;
buflen = alloc_desc_buffer(dev, index, &buffer);
if (buflen <= 0)
goto __error;
if (snd_usb_create_mixer(chip, ifnum, buffer, buflen) < 0)
if (snd_usb_create_streams(chip, ifnum, buffer, buflen) < 0 ||
snd_usb_create_mixer(chip, ifnum, buffer, buflen) < 0) {
kfree(buffer);
goto __error;
}
kfree(buffer);
} else {
if (snd_usb_create_quirk(chip, ifnum, quirk) < 0)
/* USB midi interface */
if (snd_usb_create_quirk(chip, intf, quirk) < 0)
goto __error;
}
......@@ -2142,38 +2160,20 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
chip->num_interfaces++;
up(&register_mutex);
kfree(buffer);
#ifndef OLD_USB
return 0;
#else
return chip;
#endif
__error:
up(&register_mutex);
kfree(buffer);
__err_val:
#ifndef OLD_USB
return -EIO;
#else
return NULL;
#endif
}
/*
* we need to take care of counter, since disconnection can be called also
* many times as well as usb_audio_probe().
*/
#ifndef OLD_USB
static void usb_audio_disconnect(struct usb_interface *intf)
#else
static void usb_audio_disconnect(struct usb_device *dev, void *ptr)
#endif
static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
{
#ifndef OLD_USB
void *ptr = dev_get_drvdata(&intf->dev);
#endif
snd_usb_audio_t *chip;
if (ptr == (void *)-1)
......@@ -2185,6 +2185,49 @@ static void usb_audio_disconnect(struct usb_device *dev, void *ptr)
snd_card_free(chip->card);
}
#ifdef OLD_USB
/*
* 2.4 USB kernel API
*/
static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
{
return snd_usb_audio_probe(dev, usb_ifnum_to_if(dev, ifnum), id);
}
static void usb_audio_disconnect(struct usb_device *dev, void *ptr)
{
snd_usb_audio_disconnect(dev, ptr);
}
#else
/*
* new 2.5 USB kernel API
*/
static int usb_audio_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
void *chip;
chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id);
if (chip) {
dev_set_drvdata(&intf->dev, chip);
return 0;
} else
return -EIO;
}
static void usb_audio_disconnect(struct usb_interface *intf)
{
snd_usb_audio_disconnect(interface_to_usbdev(intf),
dev_get_drvdata(&intf->dev));
}
#endif
static int __init snd_usb_audio_init(void)
{
usb_register(&usb_audio_driver);
......
......@@ -58,6 +58,8 @@
#define EP_GENERAL 0x01
#define MS_GENERAL 0x01
#define MIDI_IN_JACK 0x02
#define MIDI_OUT_JACK 0x03
/* endpoint attributes */
#define EP_ATTR_MASK 0x0c
......@@ -146,22 +148,34 @@ struct snd_usb_audio {
/*
* Information about devices with broken descriptors
*/
#define QUIRK_ANY_INTERFACE -1
#define QUIRK_MIDI_FIXED_ENDPOINT 0
#define QUIRK_MIDI_YAMAHA 1
#define QUIRK_MIDI_MIDIMAN 2
typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t;
typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t;
struct snd_usb_audio_quirk {
const char *vendor_name;
const char *product_name;
int ifnum;
int16_t ifnum;
int16_t type;
const void *data;
};
/* MIDI specific */
struct snd_usb_midi_endpoint_info {
int16_t epnum; /* ep number, -1 autodetect */
uint16_t out_cables; /* bitmask */
uint16_t in_cables; /* bitmask */
} endpoints[MIDI_MAX_ENDPOINTS];
/* data for QUIRK_MIDI_FIXED_ENDPOINT */
struct snd_usb_midi_endpoint_info {
int16_t epnum; /* ep number, -1 autodetect */
uint16_t out_cables; /* bitmask */
uint16_t in_cables; /* bitmask */
};
/* for QUIRK_MIDI_YAMAHA, data is NULL */
/* for QUIRK_MIDI_MIDIMAN, data is the number of ports */
/*
* USB MIDI sequencer device data
*/
......@@ -173,6 +187,7 @@ typedef struct snd_usb_midi_in_endpoint snd_usb_midi_in_endpoint_t;
struct snd_usb_midi {
/* filled by usbaudio.c */
snd_usb_audio_t *chip;
struct usb_interface *iface;
int ifnum;
const snd_usb_audio_quirk_t *quirk;
......
......@@ -4,6 +4,10 @@
* Copyright (c) 2002 Clemens Ladisch
* All rights reserved.
*
* Based on the OSS usb-midi driver by NAGANO Daisuke,
* NetBSD's umidi driver by Takuya SHIOZAKI,
* the "USB Device Class Definition for MIDI Devices" by Roland
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
......@@ -120,6 +124,10 @@ struct snd_usb_midi_in_endpoint {
static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep);
static const uint8_t snd_usbmidi_cin_length[] = {
0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
};
/*
* Submits the URB, with error handling.
*/
......@@ -152,9 +160,6 @@ static int snd_usbmidi_urb_error(int status)
static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep,
uint8_t packet[4])
{
static const uint8_t cin_length[] = {
0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
};
int cable = packet[0] >> 4;
usbmidi_in_port_t* port = &ep->ports[cable];
snd_seq_event_t ev;
......@@ -163,7 +168,7 @@ static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep,
return;
memset(&ev, 0, sizeof(ev));
if (snd_midi_event_encode(port->midi_event, &packet[1],
cin_length[packet[0] & 0x0f], &ev) > 0
snd_usbmidi_cin_length[packet[0] & 0x0f], &ev) > 0
&& ev.type != SNDRV_SEQ_EVENT_NONE) {
ev.source.port = port->seq_port;
ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
......@@ -199,6 +204,38 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)
}
}
/*
* Converts the data read from a Midiman device to standard USB MIDI packets.
*/
static void snd_usbmidi_in_midiman_complete(struct urb* urb)
{
if (urb->status == 0) {
uint8_t* buffer = (uint8_t*)urb->transfer_buffer;
int i;
for (i = 0; i + 4 <= urb->actual_length; i += 4) {
if (buffer[i + 3] != 0) {
/*
* snd_usbmidi_input_packet() doesn't check the
* contents of the message, so we simply use
* some random CIN with the desired length.
*/
static const uint8_t cin[4] = {
0x0, 0xf, 0x2, 0x3
};
uint8_t ctl = buffer[i + 3];
buffer[i + 3] = buffer[i + 2];
buffer[i + 2] = buffer[i + 1];
buffer[i + 1] = buffer[i + 0];
buffer[i + 0] = (ctl & 0xf0) | cin[ctl & 3];
} else {
buffer[i + 0] = 0;
}
}
}
snd_usbmidi_in_urb_complete(urb);
}
static void snd_usbmidi_out_urb_complete(struct urb* urb)
{
snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, urb->context, return);
......@@ -213,6 +250,23 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb)
spin_unlock_irqrestore(&ep->buffer_lock, flags);
}
/*
* Converts standard USB MIDI packets to what Midman devices expect.
*/
static void snd_usbmidi_convert_to_midiman(struct urb* urb)
{
uint8_t* buffer = (uint8_t*)urb->transfer_buffer;
int i;
for (i = 0; i + 4 <= urb->transfer_buffer_length; i += 4) {
uint8_t cin = buffer[i];
buffer[i + 0] = buffer[i + 1];
buffer[i + 1] = buffer[i + 2];
buffer[i + 2] = buffer[i + 3];
buffer[i + 3] = (cin & 0xf0) | snd_usbmidi_cin_length[cin & 0x0f];
}
}
/*
* This is called when some data should be transferred to the device
* (after the reception of one or more sequencer events, or after completion
......@@ -255,6 +309,9 @@ static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep)
}
if (len > 0) {
if (ep->umidi->quirk && ep->umidi->quirk->type == QUIRK_MIDI_MIDIMAN)
snd_usbmidi_convert_to_midiman(ep->urb);
ep->urb->dev = ep->umidi->chip->dev;
snd_usbmidi_submit_urb(ep->urb, GFP_ATOMIC);
}
......@@ -479,22 +536,6 @@ static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep)
snd_magic_kfree(ep);
}
#ifndef OLD_USB
/* this code is not exported from USB core anymore */
struct usb_interface *local_usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
{
int i;
for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum)
return &dev->actconfig->interface[i];
return NULL;
}
#else
#define local_usb_ifnum_to_if usb_ifnum_to_if
#endif
/*
* For Roland devices, use the alternate setting which uses interrupt
* transfers for input.
......@@ -507,7 +548,7 @@ static usb_endpoint_descriptor_t* snd_usbmidi_get_int_epd(snd_usb_midi_t* umidi,
if (umidi->chip->dev->descriptor.idVendor != 0x0582)
return NULL;
intf = local_usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
intf = umidi->iface;
if (!intf || intf->num_altsetting != 2)
return NULL;
......@@ -528,6 +569,14 @@ static usb_endpoint_descriptor_t* snd_usbmidi_get_int_epd(snd_usb_midi_t* umidi,
return &intfd->endpoint[1];
}
static usb_endpoint_descriptor_t* snd_usbmidi_get_midiman_int_epd(snd_usb_midi_t* umidi)
{
usb_interface_t* intf = umidi->iface;
if (!intf)
return NULL;
return &intf->altsetting[0].endpoint[0];
}
/*
* Creates an input endpoint, and initalizes input ports.
* ALSA ports are created later.
......@@ -551,7 +600,10 @@ static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi,
for (i = 0; i < 0x10; ++i)
ep->ports[i].seq_port = -1;
int_epd = snd_usbmidi_get_int_epd(umidi, ep_info->epnum);
if (umidi->quirk && umidi->quirk->type == QUIRK_MIDI_MIDIMAN)
int_epd = snd_usbmidi_get_midiman_int_epd(umidi);
else
int_epd = snd_usbmidi_get_int_epd(umidi, ep_info->epnum);
ep->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ep->urb) {
......@@ -696,73 +748,107 @@ static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device)
return 0;
}
/*
* Creates a sequencer port for an input/output cable pair.
*/
static int snd_usbmidi_create_port(snd_usb_midi_t* umidi,
snd_usb_midi_out_endpoint_t* out_ep,
snd_usb_midi_in_endpoint_t* in_ep,
int cable, int port_idx)
{
int cap, type, port;
snd_seq_port_callback_t port_callback;
char port_name[48];
cap = 0;
memset(&port_callback, 0, sizeof(port_callback));
port_callback.owner = THIS_MODULE;
if (out_ep) {
port_callback.event_input = snd_usbmidi_event_input;
port_callback.private_data = &out_ep->ports[cable];
cap |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
}
if (in_ep) {
cap |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
}
if (out_ep && in_ep) {
cap |= SNDRV_SEQ_PORT_CAP_DUPLEX;
}
/* TODO: read type bits from element descriptor */
type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
/* TODO: read port name from jack descriptor */
snprintf(port_name, sizeof(port_name), "%s Port %d",
umidi->chip->card->shortname, port_idx);
port = snd_seq_event_port_attach(umidi->seq_client, &port_callback,
cap, type, port_name);
if (port < 0) {
snd_printk(KERN_ERR "cannot create port (error code %d)\n", port);
return port;
}
if (in_ep)
in_ep->ports[cable].seq_port = port;
return port;
}
/*
* Creates a virmidi port emulating rawmidi for the sequencer port.
*/
static int snd_usbmidi_create_virmidi(snd_usb_midi_t* umidi, int port,
int port_idx, snd_rawmidi_t** rrmidi)
{
snd_rawmidi_t *rmidi;
snd_virmidi_dev_t *rdev;
int err;
*rrmidi = NULL;
err = snd_virmidi_new(umidi->chip->card, port_idx, &rmidi);
if (err < 0)
return err;
sprintf(rmidi->name, "%s MIDI %d", umidi->chip->card->shortname, port_idx);
rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO);
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH;
rdev->client = umidi->seq_client;
rdev->port = port;
err = snd_device_register(umidi->chip->card, rmidi);
if (err < 0) {
snd_device_free(umidi->chip->card, rmidi);
return err;
}
*rrmidi = rmidi;
return 0;
}
/*
* After input and output endpoints have been initialized, create
* the ALSA port for each input/output port pair in the endpoint.
* *port_idx is the port number, which must be unique over all endpoints.
*/
static int snd_usbmidi_create_endpoint_ports(snd_usb_midi_t* umidi, int ep, int* port_idx,
static int snd_usbmidi_create_endpoint_ports(snd_usb_midi_t* umidi,
snd_usb_midi_endpoint_t* endpoint,
int* port_idx,
snd_usb_midi_endpoint_info_t* ep_info)
{
int c, err;
int cap, type, port;
int out, in;
snd_seq_port_callback_t port_callback;
char port_name[48];
int cable;
for (c = 0; c < 0x10; ++c) {
out = ep_info->out_cables & (1 << c);
in = ep_info->in_cables & (1 << c);
for (cable = 0; cable < 0x10; ++cable) {
int port, err;
int out = ep_info->out_cables & (1 << cable);
int in = ep_info->in_cables & (1 << cable);
if (!(in || out))
continue;
cap = 0;
memset(&port_callback, 0, sizeof(port_callback));
port_callback.owner = THIS_MODULE;
if (out) {
port_callback.event_input = snd_usbmidi_event_input;
port_callback.private_data = &umidi->endpoints[ep].out->ports[c];
cap |= SNDRV_SEQ_PORT_CAP_WRITE |
SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
}
if (in) {
cap |= SNDRV_SEQ_PORT_CAP_READ |
SNDRV_SEQ_PORT_CAP_SUBS_READ;
}
if (out && in) {
cap |= SNDRV_SEQ_PORT_CAP_DUPLEX;
}
/* TODO: read type bits from element descriptor */
type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
/* TODO: read port name from jack descriptor */
snprintf(port_name, sizeof(port_name), "%s Port %d",
umidi->chip->card->shortname, *port_idx);
port = snd_seq_event_port_attach(umidi->seq_client,
&port_callback,
cap, type, port_name);
if (port < 0) {
snd_printk(KERN_ERR "cannot create port (error code %d)\n", port);
port = snd_usbmidi_create_port(umidi,
out ? endpoint->out : NULL,
in ? endpoint->in : NULL,
cable, *port_idx);
if (port < 0)
return port;
}
if (in)
umidi->endpoints[ep].in->ports[c].seq_port = port;
if (*port_idx < SNDRV_MINOR_RAWMIDIS) {
snd_rawmidi_t *rmidi;
snd_virmidi_dev_t *rdev;
err = snd_virmidi_new(umidi->chip->card, *port_idx, &rmidi);
err = snd_usbmidi_create_virmidi(umidi, port, *port_idx,
&endpoint->rmidi[cable]);
if (err < 0)
return err;
rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO);
strcpy(rmidi->name, port_name);
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH;
rdev->client = umidi->seq_client;
rdev->port = port;
err = snd_device_register(umidi->chip->card, rmidi);
if (err < 0) {
snd_device_free(umidi->chip->card, rmidi);
return err;
}
umidi->endpoints[ep].rmidi[c] = rmidi;
}
++*port_idx;
}
......@@ -792,8 +878,8 @@ static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi,
if (err < 0)
return err;
}
err = snd_usbmidi_create_endpoint_ports(umidi, i, &port_idx,
&endpoints[i]);
err = snd_usbmidi_create_endpoint_ports(umidi, &umidi->endpoints[i],
&port_idx, &endpoints[i]);
if (err < 0)
return err;
printk(KERN_INFO "snd-usb-midi: endpoint %d: created %d output and %d input ports\n",
......@@ -817,9 +903,7 @@ static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi,
usb_ms_endpoint_descriptor_t* ms_ep;
int i, epidx;
memset(endpoints, 0, sizeof(*endpoints) * MIDI_MAX_ENDPOINTS);
intf = local_usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
intf = umidi->iface;
if (!intf)
return -ENXIO;
intfd = &intf->altsetting[0];
......@@ -878,7 +962,7 @@ static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi,
usb_endpoint_descriptor_t* epd;
if (endpoint->epnum == -1) {
intf = local_usb_ifnum_to_if(umidi->chip->dev, umidi->ifnum);
intf = umidi->iface;
if (!intf || intf->num_altsetting < 1)
return -ENOENT;
intfd = intf->altsetting;
......@@ -890,6 +974,120 @@ static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi,
return 0;
}
/*
* Detects the endpoints and ports of Yamaha devices.
*/
static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi,
snd_usb_midi_endpoint_info_t* endpoint)
{
usb_interface_t* intf;
usb_interface_descriptor_t* intfd;
uint8_t* cs_desc;
intf = umidi->iface;
if (!intf)
return -ENOENT;
intfd = intf->altsetting;
if (intfd->bNumEndpoints < 1)
return -ENOENT;
for (cs_desc = intfd->extra;
cs_desc < intfd->extra + intfd->extralen && cs_desc[0] >= 2;
cs_desc += cs_desc[0]) {
if (cs_desc[1] == CS_AUDIO_INTERFACE) {
if (cs_desc[2] == MIDI_IN_JACK)
endpoint->in_cables = (endpoint->in_cables << 1) | 1;
else if (cs_desc[2] == MIDI_OUT_JACK)
endpoint->out_cables = (endpoint->out_cables << 1) | 1;
}
}
if (!endpoint->in_cables && !endpoint->out_cables)
return -ENOENT;
endpoint->epnum = -1;
return snd_usbmidi_detect_endpoint(umidi, endpoint);
}
/*
* Creates the endpoints and their ports for Midiman devices.
*/
static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, int ports)
{
snd_usb_midi_endpoint_info_t ep_info;
usb_interface_t* intf;
usb_interface_descriptor_t* intfd;
usb_endpoint_descriptor_t* epd;
int cable, err;
intf = umidi->iface;
if (!intf)
return -ENOENT;
intfd = intf->altsetting;
if (intfd->bNumEndpoints < (ports > 1 ? 5 : 3)) {
snd_printdd(KERN_ERR "not enough endpoints\n");
return -ENOENT;
}
epd = &intfd->endpoint[0];
if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN ||
(epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) {
snd_printdd(KERN_ERR "endpoint[0] isn't interrupt\n");
return -ENXIO;
}
epd = &intfd->endpoint[2];
if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_OUT ||
(epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
snd_printdd(KERN_ERR "endpoint[2] isn't bulk output\n");
return -ENXIO;
}
if (ports > 1) {
epd = &intfd->endpoint[4];
if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_OUT ||
(epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
snd_printdd(KERN_ERR "endpoint[4] isn't bulk output\n");
return -ENXIO;
}
}
ep_info.epnum = intfd->endpoint[2].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
ep_info.out_cables = 0x5555 & ((1 << ports) - 1);
err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]);
if (err < 0)
return err;
ep_info.epnum = intfd->endpoint[0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
ep_info.in_cables = (1 << ports) - 1;
err = snd_usbmidi_in_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]);
if (err < 0)
return err;
umidi->endpoints[0].in->urb->complete = snd_usbmidi_in_midiman_complete;
if (ports > 1) {
ep_info.epnum = intfd->endpoint[4].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
ep_info.out_cables = 0xaaaa & ((1 << ports) - 1);
err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[1]);
if (err < 0)
return err;
}
for (cable = 0; cable < ports; ++cable) {
int port = snd_usbmidi_create_port(umidi,
umidi->endpoints[cable & 1].out,
umidi->endpoints[0].in,
cable, cable);
if (port < 0)
return port;
if (cable < SNDRV_MINOR_RAWMIDIS) {
int err = snd_usbmidi_create_virmidi(umidi, port, cable,
&umidi->endpoints[0].rmidi[cable]);
if (err < 0)
return err;
}
}
return 0;
}
/*
* Initialize the sequencer device.
*/
......@@ -912,6 +1110,7 @@ static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device)
if (umidi->seq_client < 0)
return umidi->seq_client;
/* set the client name */
memset(&client_info, 0, sizeof(client_info));
client_info.client = umidi->seq_client;
client_info.type = KERNEL_CLIENT;
......@@ -935,17 +1134,37 @@ static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device)
SNDRV_SEQ_IOCTL_SET_CLIENT_INFO,
&client_info);
if (umidi->quirk) {
memcpy(endpoints, umidi->quirk->endpoints, sizeof(endpoints));
err = snd_usbmidi_detect_endpoint(umidi, &endpoints[0]);
} else {
/* detect the endpoint(s) to use */
memset(endpoints, 0, sizeof(endpoints));
if (!umidi->quirk) {
err = snd_usbmidi_get_ms_info(umidi, endpoints);
} else {
switch (umidi->quirk->type) {
case QUIRK_MIDI_FIXED_ENDPOINT:
memcpy(&endpoints[0], umidi->quirk->data,
sizeof(snd_usb_midi_endpoint_info_t));
err = snd_usbmidi_detect_endpoint(umidi, &endpoints[0]);
break;
case QUIRK_MIDI_YAMAHA:
err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]);
break;
case QUIRK_MIDI_MIDIMAN:
err = 0;
break;
default:
snd_printd(KERN_ERR "invalid quirk type %d\n", umidi->quirk->type);
err = -ENXIO;
break;
}
}
if (err < 0) {
snd_usbmidi_seq_device_delete(seq_device);
return err;
/* create ports */
if (err >= 0) {
if (umidi->quirk && umidi->quirk->type == QUIRK_MIDI_MIDIMAN)
err = snd_usbmidi_create_endpoints_midiman(umidi, (int)umidi->quirk->data);
else
err = snd_usbmidi_create_endpoints(umidi, endpoints);
}
err = snd_usbmidi_create_endpoints(umidi, endpoints);
if (err < 0) {
snd_usbmidi_seq_device_delete(seq_device);
return err;
......
......@@ -26,45 +26,143 @@
* In a perfect world, this file would be empty.
*/
#define USB_DEVICE_VENDOR_SPEC(vend, prod) \
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | \
USB_DEVICE_ID_MATCH_PRODUCT | \
USB_DEVICE_ID_MATCH_INT_CLASS, \
.idVendor = vend, \
.idProduct = prod, \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
/* Yamaha devices */
{
/* from NetBSD's umidi driver */
USB_DEVICE(0x0499, 0x1000), /* Yamaha UX256 */
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1000),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0xffff,
.in_cables = 0x00ff
}
}
.vendor_name = "Yamaha",
.product_name = "UX256",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
/* from Nagano Daisuke's usb-midi driver */
USB_DEVICE(0x0499, 0x1001), /* Yamaha MU1000 */
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1001),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.ifnum = 0,
.endpoints = {
{
.epnum = 1,
.out_cables = 0x000f,
.in_cables = 0x0001
}
}
.vendor_name = "Yamaha",
.product_name = "MU1000",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1002),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "MU2000",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1003),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "MU500",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1004),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "UW500",
.ifnum = 3,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1005),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "MOTIF6",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1006),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "MOTIF7",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1007),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "MOTIF8",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1008),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "UX96",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1009),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "UX16",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x100a),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "EOS BX",
.ifnum = 3,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x100e),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "S08",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x100f),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "CLP-150",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0499, 0x1010),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Yamaha",
.product_name = "CLP-170",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_YAMAHA
}
},
/*
* I don't know whether the following Yamaha devices need entries or not:
* 0x1002 MU2000 0x1008 UX96
* 0x1003 MU500 0x1009 UX16
* 0x1004 UW500 0x100e S08
* 0x1005 MOTIF6 0x100f CLP-150
* 0x1006 MOTIF7 0x1010 CLP-170
* 0x1007 MOTIF8
*/
/*
* Once upon a time people thought, "Wouldn't it be nice if there was a
......@@ -79,259 +177,316 @@
* class-specific descriptors.
*/
{
USB_DEVICE(0x0582, 0x0000),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0000),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "UA-100",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0007,
.in_cables = 0x0007
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0007,
.in_cables = 0x0007
}
}
},
{
USB_DEVICE(0x0582, 0x0002),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0002),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-4",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
}
},
{
USB_DEVICE(0x0582, 0x0003),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0003),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "SC-8850",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x003f,
.in_cables = 0x003f
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x003f,
.in_cables = 0x003f
}
}
},
{
USB_DEVICE(0x0582, 0x0004),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0004),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "U-8",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
}
},
{
USB_DEVICE(0x0582, 0x0005),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0005),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-2",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
}
},
{
USB_DEVICE(0x0582, 0x0007),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0007),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "SC-8820",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0013,
.in_cables = 0x0013
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0013,
.in_cables = 0x0013
}
}
},
{
USB_DEVICE(0x0582, 0x0008),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0008),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "PC-300",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
}
},
{
USB_DEVICE(0x0582, 0x0009),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0009),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-1",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
}
},
{
USB_DEVICE(0x0582, 0x000b),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x000b),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "SK-500",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0013,
.in_cables = 0x0013
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0013,
.in_cables = 0x0013
}
}
},
{
USB_DEVICE(0x0582, 0x000c),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x000c),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "SC-D70",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0007,
.in_cables = 0x0007
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0007,
.in_cables = 0x0007
}
}
},
{
USB_DEVICE(0x0582, 0x0012),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0012),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Roland",
.product_name = "XV-5050",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0001,
.in_cables = 0x0001
}
}
},
{
USB_DEVICE(0x0582, 0x0014),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0014),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-880",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x01ff,
.in_cables = 0x01ff
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x01ff,
.in_cables = 0x01ff
}
}
},
{
USB_DEVICE(0x0582, 0x0016),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0016),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "SD-90",
.ifnum = 2,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
}
},
{
USB_DEVICE(0x0582, 0x0023),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0023),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UM-550",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x003f,
.in_cables = 0x003f
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x003f,
.in_cables = 0x003f
}
}
},
{
USB_DEVICE(0x0582, 0x0027),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0027),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "SD-20",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0007
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0007
}
}
},
{
USB_DEVICE(0x0582, 0x0029),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x0029),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "SD-80",
.ifnum = 0,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x000f,
.in_cables = 0x000f
}
}
},
{
USB_DEVICE(0x0582, 0x002b),
USB_DEVICE_VENDOR_SPEC(0x0582, 0x002b),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UA-700",
.ifnum = 3,
.endpoints = {
{
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const snd_usb_midi_endpoint_info_t) {
.epnum = -1,
.out_cables = 0x0003,
.in_cables = 0x0003
}
}
},
/* Midiman/M-Audio devices */
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1002),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "M-Audio",
.product_name = "MidiSport 2x2",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_MIDIMAN,
.data = (void*) 2
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1011),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "M-Audio",
.product_name = "MidiSport 1x1",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_MIDIMAN,
.data = (void*) 1
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1015),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "M-Audio",
.product_name = "Keystation",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_MIDIMAN,
.data = (void*) 1
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1021),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "M-Audio",
.product_name = "MidiSport 4x4",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_MIDIMAN,
.data = (void*) 4
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x1033),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "M-Audio",
.product_name = "MidiSport 8x8",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_MIDI_MIDIMAN,
.data = (void*) 9
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2001),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "M-Audio",
.product_name = "Quattro",
.ifnum = 9,
.type = QUIRK_MIDI_MIDIMAN,
.data = (void*) 1
}
},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2003),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "M-Audio",
.product_name = "AudioPhile",
.ifnum = 9,
.type = QUIRK_MIDI_MIDIMAN,
.data = (void*) 1
}
},
#endif /* CONFIG_SND_SEQUENCER(_MODULE) */
#undef USB_DEVICE_VENDOR_SPEC
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment