diff options
| author | Linus Torvalds <torvalds@athlon.transmeta.com> | 2002-02-04 20:17:21 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@athlon.transmeta.com> | 2002-02-04 20:17:21 -0800 |
| commit | 7df131525f431f502873361fa2f8da2039d96c79 (patch) | |
| tree | f7253fdcf2782e99998c5102f87b84fb966674ff /drivers | |
| parent | 70a8be476e663526c3cb17a157c17ccf4fca5bd4 (diff) | |
v2.4.9.6 -> v2.4.9.7
- Alan Cox: big driver/mips sync
- Andries Brouwer, Christoph Hellwig: more gendisk fixups
- Tobias Ringstrom: tulip driver workaround for DC21143 erratum
Diffstat (limited to 'drivers')
121 files changed, 16465 insertions, 3640 deletions
diff --git a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c index 5502165c4c2a..779a38ab5147 100644 --- a/drivers/acorn/block/mfmhd.c +++ b/drivers/acorn/block/mfmhd.c @@ -1311,15 +1311,13 @@ void xd_set_geometry(kdev_t dev, unsigned char secsptrack, unsigned char heads, } static struct gendisk mfm_gendisk = { - MAJOR_NR, /* Major number */ - "mfm", /* Major name */ - 6, /* Bits to shift to get real from partition */ - 1 << 6, /* Number of partitions per real */ - mfm, /* hd struct */ - mfm_sizes, /* block sizes */ - 0, /* number */ - (void *) mfm_info, /* internal */ - NULL /* next */ + major: MAJOR_NR, + major_name: "mfm", + minor_shift: 6, + max_p: 1 << 6, + part: mfm, + sizes: mfm_sizes, + real_devices: (void *)mfm_info, }; static struct block_device_operations mfm_fops = diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index afda5fa3a405..1f4b9bce5c72 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -1883,7 +1883,6 @@ static int DAC960_MergeRequestsFunction(RequestQueue_T *RequestQueue, static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller) { int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber; - GenericDiskInfo_T *GenericDiskInfo; RequestQueue_T *RequestQueue; int MinorNumber; /* diff --git a/drivers/block/Makefile b/drivers/block/Makefile index a98418e00263..04fb8fe66e37 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -10,7 +10,7 @@ O_TARGET := block.o -export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o +export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o genhd.o obj-y := ll_rw_blk.o blkpg.o genhd.o elevator.o diff --git a/drivers/block/acsi.c b/drivers/block/acsi.c index dbd637a5e89f..6d44e211f1f8 100644 --- a/drivers/block/acsi.c +++ b/drivers/block/acsi.c @@ -1386,16 +1386,14 @@ static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd ) extern struct block_device_operations acsi_fops; static struct gendisk acsi_gendisk = { - MAJOR_NR, /* Major number */ - "ad", /* Major name */ - 4, /* Bits to shift to get real from partition */ - 1 << 4, /* Number of partitions per real */ - acsi_part, /* hd struct */ - acsi_sizes, /* block sizes */ - 0, /* number */ - (void *)acsi_info, /* internal */ - NULL, /* next */ - &acsi_fops, /* file operations */ + major: MAJOR_NR, + major_name: "ad", + minor_shift: 4, + max_p: 1 << 4, + part: acsi_part, + sizes: acsi_sizes, + real_devices: (void *)acsi_info, + fops: &acsi_fops, }; #define MAX_SCSI_DEVICE_CODE 10 diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 4503fcdb2356..843dd8f8e21f 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -10,47 +10,101 @@ * (linux@arm.uk.linux.org) */ +/* + * TODO: rip out the remaining init crap from this file --hch + */ + #include <linux/config.h> +#include <linux/module.h> #include <linux/fs.h> #include <linux/genhd.h> #include <linux/kernel.h> #include <linux/blk.h> #include <linux/init.h> +#include <linux/spinlock.h> + + +static rwlock_t gendisk_lock; + +/* + * Global kernel list of partitioning information. + * + * XXX: you should _never_ access this directly. + * the only reason this is exported is source compatiblity. + */ +/*static*/ struct gendisk *gendisk_head; +EXPORT_SYMBOL(gendisk_head); -struct gendisk *gendisk_head; +/** + * add_gendisk - add partitioning information to kernel list + * @gp: per-device partitioning information + * + * This function registers the partitioning information in @gp + * with the kernel. + */ void add_gendisk(struct gendisk *gp) { + write_lock(&gendisk_lock); gp->next = gendisk_head; gendisk_head = gp; + write_unlock(&gendisk_lock); } +EXPORT_SYMBOL(add_gendisk); + + +/** + * del_gendisk - remove partitioning information from kernel list + * @gp: per-device partitioning information + * + * This function unregisters the partitioning information in @gp + * with the kernel. + */ void del_gendisk(struct gendisk *gp) { struct gendisk **gpp; + write_lock(&gendisk_lock); for (gpp = &gendisk_head; *gpp; gpp = &((*gpp)->next)) if (*gpp == gp) break; if (*gpp) *gpp = (*gpp)->next; + write_unlock(&gendisk_lock); } +EXPORT_SYMBOL(del_gendisk); + + +/** + * get_gendisk - get partitioning information for a given device + * @dev: device to get partitioning information for + * + * This function gets the structure containing partitioning + * information for the given device @dev. + */ struct gendisk * get_gendisk(kdev_t dev) { struct gendisk *gp = NULL; int maj = MAJOR(dev); + read_lock(&gendisk_lock); for (gp = gendisk_head; gp; gp = gp->next) if (gp->major == maj) - return gp; - return NULL; + break; + read_unlock(&gendisk_lock); + + return gp; } +EXPORT_SYMBOL(get_gendisk); + + #ifdef CONFIG_PROC_FS int get_partition_list(char *page, char **start, off_t offset, int count) @@ -60,6 +114,7 @@ get_partition_list(char *page, char **start, off_t offset, int count) int len, n; len = sprintf(page, "major minor #blocks name\n\n"); + read_lock(&gendisk_lock); for (gp = gendisk_head; gp; gp = gp->next) { for (n = 0; n < (gp->nr_real << gp->minor_shift); n++) { if (gp->part[n].nr_sects == 0) @@ -77,6 +132,7 @@ get_partition_list(char *page, char **start, off_t offset, int count) } out: + read_unlock(&gendisk_lock); *start = page + offset; len -= offset; if (len < 0) @@ -102,6 +158,7 @@ extern int cpqarray_init(void); int __init device_init(void) { + rwlock_init(&gendisk_lock); blk_dev_init(); sti(); #ifdef CONFIG_I2O diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index 0d5763f242ed..43c6510f41cc 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -343,16 +343,13 @@ static char *pd_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR", extern struct block_device_operations pd_fops; static struct gendisk pd_gendisk = { - PD_MAJOR, /* Major number */ - PD_NAME, /* Major name */ - PD_BITS, /* Bits to shift to get real from partition */ - PD_PARTNS, /* Number of partitions per real */ - pd_hd, /* hd struct */ - pd_sizes, /* block sizes */ - 0, /* number */ - NULL, /* internal */ - NULL, /* next */ - &pd_fops, /* block device operations */ + major: PD_MAJOR, + major_name: PD_NAME, + minor_shift: PD_BITS, + max_p: PD_PARTNS, + part: pd_hd, + sizes: pd_sizes, + fops: &pd_fops, }; static struct block_device_operations pd_fops = { diff --git a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c index 9be57ac1558e..5845927e04ab 100644 --- a/drivers/block/ps2esdi.c +++ b/drivers/block/ps2esdi.c @@ -157,16 +157,14 @@ static struct block_device_operations ps2esdi_fops = static struct gendisk ps2esdi_gendisk = { - MAJOR_NR, /* Major number */ - "ed", /* Major name */ - 6, /* Bits to shift to get real from partition */ - 1 << 6, /* Number of partitions per real disk */ - ps2esdi, /* hd struct */ - ps2esdi_sizes, /* block sizes */ - 0, /* number */ - (void *) ps2esdi_info, /* internal */ - NULL, /* next */ - &ps2esdi_fops, /* file operations */ + major: MAJOR_NR, + major_name: "ed", + minor_shift: 6, + max_p: 1 << 6, + part: ps2esdi, + sizes: ps2esdi_sizes, + real_devices: (void *)ps2esdi_info, + fops: &ps2esdi_fops, }; /* initialization routine called by ll_rw_blk.c */ @@ -222,14 +220,13 @@ int init_module(void) { void cleanup_module(void) { - if(ps2esdi_slot) - { + if(ps2esdi_slot) { mca_mark_as_unused(ps2esdi_slot); mca_set_adapter_procfn(ps2esdi_slot, NULL, NULL); } release_region(io_base, 4); free_dma(dma_arb_level); - free_irq(PS2ESDI_IRQ, NULL) + free_irq(PS2ESDI_IRQ, NULL); devfs_unregister_blkdev(MAJOR_NR, "ed"); del_gendisk(&ps2esdi_gendisk); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 843478e29de7..7ccda2a70567 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -259,7 +259,7 @@ static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un /* special: we want to release the ramdisk memory, it's not like with the other blockdevices where this ioctl only flushes away the buffer cache. */ - if ((atomic_read(rd_bdev[minor]->bd_openers) > 2)) + if ((atomic_read(&rd_bdev[minor]->bd_openers) > 2)) return -EBUSY; destroy_buffers(inode->i_rdev); rd_blocksizes[minor] = 0; @@ -372,7 +372,7 @@ static void __exit rd_cleanup (void) struct block_device *bdev = rd_bdev[i]; rd_bdev[i] = NULL; if (bdev) { - blkdev_put(bdev); + blkdev_put(bdev, BDEV_FILE); bdput(bdev); } destroy_buffers(MKDEV(MAJOR_NR, i)); diff --git a/drivers/block/xd.c b/drivers/block/xd.c index 905223a9ab33..12e4877cedd3 100644 --- a/drivers/block/xd.c +++ b/drivers/block/xd.c @@ -126,17 +126,16 @@ static int xd_maxsect[XD_MAXDRIVES << 6]; extern struct block_device_operations xd_fops; static struct gendisk xd_gendisk = { - MAJOR_NR, /* Major number */ - "xd", /* Major name */ - 6, /* Bits to shift to get real from partition */ - 1 << 6, /* Number of partitions per real */ - xd_struct, /* hd struct */ - xd_sizes, /* block sizes */ - 0, /* number */ - (void *) xd_info, /* internal */ - NULL, /* next */ - &xd_fops, /* file operations */ + major: MAJOR_NR, + major_name: "xd", + minor_shift: 6, + max_p: 1 << 6, + part: xd_struct, + sizes: xd_sizes, + real_devices: (void *)xd_info, + fops: &xd_fops, }; + static struct block_device_operations xd_fops = { open: xd_open, release: xd_release, diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c index 702f687709b8..6b35b013bf96 100644 --- a/drivers/cdrom/aztcd.c +++ b/drivers/cdrom/aztcd.c @@ -167,7 +167,7 @@ #include <linux/version.h> -#define MAJOR_NR AZTECH_CDROM_MAJOR +#define MAJOR_NR AZTECH_CDROM_MAJOR #include <linux/blk.h> #include "aztcd.h" @@ -191,7 +191,7 @@ #include <asm/io.h> #include <asm/uaccess.h> -static int aztcd_blocksizes[1] = {2048}; +static int aztcd_blocksizes[1] = { 2048 }; /*########################################################################### @@ -200,7 +200,7 @@ static int aztcd_blocksizes[1] = {2048}; */ #define SET_TIMER(func, jifs) delay_timer.expires = jiffies + (jifs); \ delay_timer.function = (void *) (func); \ - add_timer(&delay_timer); + add_timer(&delay_timer); #define CLEAR_TIMER del_timer(&delay_timer); @@ -213,17 +213,17 @@ static int aztcd_blocksizes[1] = {2048}; #define SWITCH_IDE_SLAVE outb_p(0xa0,azt_port+6); \ outb_p(0x10,azt_port+6); \ outb_p(0x00,azt_port+7); \ - outb_p(0x10,azt_port+6); + outb_p(0x10,azt_port+6); #define SWITCH_IDE_MASTER outb_p(0xa0,azt_port+6); #if 0 #define AZT_TEST -#define AZT_TEST1 /* <int-..> */ -#define AZT_TEST2 /* do_aztcd_request */ -#define AZT_TEST3 /* AZT_S_state */ -#define AZT_TEST4 /* QUICK_LOOP-counter */ -#define AZT_TEST5 /* port(1) state */ +#define AZT_TEST1 /* <int-..> */ +#define AZT_TEST2 /* do_aztcd_request */ +#define AZT_TEST3 /* AZT_S_state */ +#define AZT_TEST4 /* QUICK_LOOP-counter */ +#define AZT_TEST5 /* port(1) state */ #define AZT_DEBUG #define AZT_DEBUG_MULTISESSION #endif @@ -237,25 +237,23 @@ static int aztcd_blocksizes[1] = {2048}; #define READ_TIMEOUT 3000 -#define azt_port aztcd /*needed for the modutils*/ +#define azt_port aztcd /*needed for the modutils */ /*########################################################################## Type Definitions ########################################################################## */ -enum azt_state_e -{ AZT_S_IDLE, /* 0 */ - AZT_S_START, /* 1 */ - AZT_S_MODE, /* 2 */ - AZT_S_READ, /* 3 */ - AZT_S_DATA, /* 4 */ - AZT_S_STOP, /* 5 */ - AZT_S_STOPPING /* 6 */ +enum azt_state_e { AZT_S_IDLE, /* 0 */ + AZT_S_START, /* 1 */ + AZT_S_MODE, /* 2 */ + AZT_S_READ, /* 3 */ + AZT_S_DATA, /* 4 */ + AZT_S_STOP, /* 5 */ + AZT_S_STOPPING /* 6 */ }; -enum azt_read_modes -{ AZT_MODE_0, /*read mode for audio disks, not supported by Aztech firmware*/ - AZT_MODE_1, /*read mode for normal CD-ROMs*/ - AZT_MODE_2 /*read mode for XA CD-ROMs*/ +enum azt_read_modes { AZT_MODE_0, /*read mode for audio disks, not supported by Aztech firmware */ + AZT_MODE_1, /*read mode for normal CD-ROMs */ + AZT_MODE_2 /*read mode for XA CD-ROMs */ }; /*########################################################################## @@ -264,20 +262,20 @@ enum azt_read_modes */ static int aztPresent = 0; -static volatile int azt_transfer_is_active=0; +static volatile int azt_transfer_is_active = 0; -static char azt_buf[CD_FRAMESIZE_RAW*AZT_BUF_SIZ];/*buffer for block size conversion*/ +static char azt_buf[CD_FRAMESIZE_RAW * AZT_BUF_SIZ]; /*buffer for block size conversion */ #if AZT_PRIVATE_IOCTLS -static char buf[CD_FRAMESIZE_RAW]; /*separate buffer for the ioctls*/ +static char buf[CD_FRAMESIZE_RAW]; /*separate buffer for the ioctls */ #endif static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn; static volatile int azt_buf_in, azt_buf_out = -1; -static volatile int azt_error=0; -static int azt_open_count=0; +static volatile int azt_error = 0; +static int azt_open_count = 0; static volatile enum azt_state_e azt_state = AZT_S_IDLE; #ifdef AZT_TEST3 -static volatile enum azt_state_e azt_state_old = AZT_S_STOP; +static volatile enum azt_state_e azt_state_old = AZT_S_STOP; static volatile int azt_st_old = 0; #endif static volatile enum azt_read_modes azt_read_mode = AZT_MODE_1; @@ -291,9 +289,9 @@ MODULE_PARM(azt_port, "i"); static int azt_port_auto[16] = AZT_BASE_AUTO; -static char azt_cont = 0; -static char azt_init_end = 0; -static char azt_auto_eject = AZT_AUTO_EJECT; +static char azt_cont = 0; +static char azt_init_end = 0; +static char azt_auto_eject = AZT_AUTO_EJECT; static int AztTimeout, AztTries; static DECLARE_WAIT_QUEUE_HEAD(azt_waitq); @@ -303,7 +301,7 @@ static struct azt_DiskInfo DiskInfo; static struct azt_Toc Toc[MAX_TRACKS]; static struct azt_Play_msf azt_Play; -static int aztAudioStatus = CDROM_AUDIO_NO_STATUS; +static int aztAudioStatus = CDROM_AUDIO_NO_STATUS; static char aztDiskChanged = 1; static char aztTocUpToDate = 0; @@ -316,50 +314,51 @@ static int aztCmd = 0; ########################################################################### */ /* CDROM Drive Low Level I/O Functions */ -void op_ok(void); -void pa_ok(void); -void sten_low(void); -void dten_low(void); -void statusAzt(void); +void op_ok(void); +void pa_ok(void); +void sten_low(void); +void dten_low(void); +void statusAzt(void); static void aztStatTimer(void); /* CDROM Drive Command Functions */ -static int aztSendCmd(int cmd); -static int sendAztCmd(int cmd, struct azt_Play_msf *params); -static int aztSeek(struct azt_Play_msf *params); -static int aztSetDiskType(int type); -static int aztStatus(void); -static int getAztStatus(void); -static int aztPlay(struct azt_Play_msf *arg); +static int aztSendCmd(int cmd); +static int sendAztCmd(int cmd, struct azt_Play_msf *params); +static int aztSeek(struct azt_Play_msf *params); +static int aztSetDiskType(int type); +static int aztStatus(void); +static int getAztStatus(void); +static int aztPlay(struct azt_Play_msf *arg); static void aztCloseDoor(void); static void aztLockDoor(void); static void aztUnlockDoor(void); -static int aztGetValue(unsigned char *result); -static int aztGetQChannelInfo(struct azt_Toc *qp); -static int aztUpdateToc(void); -static int aztGetDiskInfo(void); -#if AZT_MULTISESSION - static int aztGetMultiDiskInfo(void); +static int aztGetValue(unsigned char *result); +static int aztGetQChannelInfo(struct azt_Toc *qp); +static int aztUpdateToc(void); +static int aztGetDiskInfo(void); +#if AZT_MULTISESSION +static int aztGetMultiDiskInfo(void); #endif -static int aztGetToc(int multi); +static int aztGetToc(int multi); /* Kernel Interface Functions */ -static int check_aztcd_media_change(kdev_t full_dev); -static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); +static int check_aztcd_media_change(kdev_t full_dev); +static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, + unsigned long arg); static void azt_transfer(void); static void do_aztcd_request(request_queue_t *); static void azt_invalidate_buffers(void); -int aztcd_open(struct inode *ip, struct file *fp); +int aztcd_open(struct inode *ip, struct file *fp); -static int aztcd_release(struct inode * inode, struct file * file); +static int aztcd_release(struct inode *inode, struct file *file); -int aztcd_init(void); +int aztcd_init(void); static struct block_device_operations azt_fops = { - open: aztcd_open, - release: aztcd_release, - ioctl: aztcd_ioctl, - check_media_change: check_aztcd_media_change, + open:aztcd_open, + release:aztcd_release, + ioctl:aztcd_ioctl, + check_media_change:check_aztcd_media_change, }; /* Aztcd State Machine: Controls Drive Operating State */ @@ -369,7 +368,7 @@ static void azt_poll(void); static void azt_hsg2msf(long hsg, struct msf *msf); static long azt_msf2hsg(struct msf *mp); static void azt_bin2bcd(unsigned char *p); -static int azt_bcd2bin(unsigned char bcd); +static int azt_bcd2bin(unsigned char bcd); /*########################################################################## CDROM Drive Low Level I/O Functions @@ -380,53 +379,64 @@ static int azt_bcd2bin(unsigned char bcd); /* Wait for OP_OK = drive answers with AFL_OP_OK after receiving a command*/ # define OP_OK op_ok() void op_ok(void) -{ aztTimeOutCount=0; - do { aztIndatum=inb(DATA_PORT); - aztTimeOutCount++; - if (aztTimeOutCount>=AZT_TIMEOUT) - { printk("aztcd: Error Wait OP_OK\n"); - break; - } - } while (aztIndatum!=AFL_OP_OK); +{ + aztTimeOutCount = 0; + do { + aztIndatum = inb(DATA_PORT); + aztTimeOutCount++; + if (aztTimeOutCount >= AZT_TIMEOUT) { + printk("aztcd: Error Wait OP_OK\n"); + break; + } + } while (aztIndatum != AFL_OP_OK); } /* Wait for PA_OK = drive answers with AFL_PA_OK after receiving parameters*/ # define PA_OK pa_ok() void pa_ok(void) -{ aztTimeOutCount=0; - do { aztIndatum=inb(DATA_PORT); - aztTimeOutCount++; - if (aztTimeOutCount>=AZT_TIMEOUT) - { printk("aztcd: Error Wait PA_OK\n"); - break; - } - } while (aztIndatum!=AFL_PA_OK); +{ + aztTimeOutCount = 0; + do { + aztIndatum = inb(DATA_PORT); + aztTimeOutCount++; + if (aztTimeOutCount >= AZT_TIMEOUT) { + printk("aztcd: Error Wait PA_OK\n"); + break; + } + } while (aztIndatum != AFL_PA_OK); } - + /* Wait for STEN=Low = handshake signal 'AFL_.._OK available or command executed*/ # define STEN_LOW sten_low() void sten_low(void) -{ aztTimeOutCount=0; - do { aztIndatum=inb(STATUS_PORT); - aztTimeOutCount++; - if (aztTimeOutCount>=AZT_TIMEOUT) - { if (azt_init_end) printk("aztcd: Error Wait STEN_LOW commands:%x\n",aztCmd); - break; - } - } while (aztIndatum&AFL_STATUS); +{ + aztTimeOutCount = 0; + do { + aztIndatum = inb(STATUS_PORT); + aztTimeOutCount++; + if (aztTimeOutCount >= AZT_TIMEOUT) { + if (azt_init_end) + printk + ("aztcd: Error Wait STEN_LOW commands:%x\n", + aztCmd); + break; + } + } while (aztIndatum & AFL_STATUS); } /* Wait for DTEN=Low = handshake signal 'Data available'*/ # define DTEN_LOW dten_low() void dten_low(void) -{ aztTimeOutCount=0; - do { aztIndatum=inb(STATUS_PORT); - aztTimeOutCount++; - if (aztTimeOutCount>=AZT_TIMEOUT) - { printk("aztcd: Error Wait DTEN_OK\n"); - break; - } - } while (aztIndatum&AFL_DATA); +{ + aztTimeOutCount = 0; + do { + aztIndatum = inb(STATUS_PORT); + aztTimeOutCount++; + if (aztTimeOutCount >= AZT_TIMEOUT) { + printk("aztcd: Error Wait DTEN_OK\n"); + break; + } + } while (aztIndatum & AFL_DATA); } /* @@ -435,25 +445,29 @@ void dten_low(void) */ #define STEN_LOW_WAIT statusAzt() void statusAzt(void) -{ AztTimeout = AZT_STATUS_DELAY; - SET_TIMER(aztStatTimer, HZ/100); - sleep_on(&azt_waitq); - if (AztTimeout <= 0) printk("aztcd: Error Wait STEN_LOW_WAIT command:%x\n",aztCmd); - return; +{ + AztTimeout = AZT_STATUS_DELAY; + SET_TIMER(aztStatTimer, HZ / 100); + sleep_on(&azt_waitq); + if (AztTimeout <= 0) + printk("aztcd: Error Wait STEN_LOW_WAIT command:%x\n", + aztCmd); + return; } static void aztStatTimer(void) -{ if (!(inb(STATUS_PORT) & AFL_STATUS)) - { wake_up(&azt_waitq); - return; - } - AztTimeout--; - if (AztTimeout <= 0) - { wake_up(&azt_waitq); - printk("aztcd: Error aztStatTimer: Timeout\n"); - return; - } - SET_TIMER(aztStatTimer, HZ/100); +{ + if (!(inb(STATUS_PORT) & AFL_STATUS)) { + wake_up(&azt_waitq); + return; + } + AztTimeout--; + if (AztTimeout <= 0) { + wake_up(&azt_waitq); + printk("aztcd: Error aztStatTimer: Timeout\n"); + return; + } + SET_TIMER(aztStatTimer, HZ / 100); } /*########################################################################## @@ -464,111 +478,125 @@ static void aztStatTimer(void) * Send a single command, return -1 on error, else 0 */ static int aztSendCmd(int cmd) -{ unsigned char data; - int retry; +{ + unsigned char data; + int retry; #ifdef AZT_DEBUG - printk("aztcd: Executing command %x\n",cmd); + printk("aztcd: Executing command %x\n", cmd); #endif - if ((azt_port==0x1f0)||(azt_port==0x170)) - SWITCH_IDE_SLAVE; /*switch IDE interface to slave configuration*/ - - aztCmd=cmd; - outb(POLLED,MODE_PORT); - do { if (inb(STATUS_PORT)&AFL_STATUS) break; - inb(DATA_PORT); /* if status left from last command, read and */ - } while (1); /* discard it */ - do { if (inb(STATUS_PORT)&AFL_DATA) break; - inb(DATA_PORT); /* if data left from last command, read and */ - } while (1); /* discard it */ - for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++) - { outb((unsigned char) cmd,CMD_PORT); - STEN_LOW; - data=inb(DATA_PORT); - if (data==AFL_OP_OK) - { return 0;} /*OP_OK?*/ - if (data==AFL_OP_ERR) - { STEN_LOW; - data=inb(DATA_PORT); - printk("### Error 1 aztcd: aztSendCmd %x Error Code %x\n",cmd,data); - } - } - if (retry>=AZT_RETRY_ATTEMPTS) - { printk("### Error 2 aztcd: aztSendCmd %x \n",cmd); - azt_error=0xA5; - } - RETURNM("aztSendCmd",-1); + if ((azt_port == 0x1f0) || (azt_port == 0x170)) + SWITCH_IDE_SLAVE; /*switch IDE interface to slave configuration */ + + aztCmd = cmd; + outb(POLLED, MODE_PORT); + do { + if (inb(STATUS_PORT) & AFL_STATUS) + break; + inb(DATA_PORT); /* if status left from last command, read and */ + } while (1); /* discard it */ + do { + if (inb(STATUS_PORT) & AFL_DATA) + break; + inb(DATA_PORT); /* if data left from last command, read and */ + } while (1); /* discard it */ + for (retry = 0; retry < AZT_RETRY_ATTEMPTS; retry++) { + outb((unsigned char) cmd, CMD_PORT); + STEN_LOW; + data = inb(DATA_PORT); + if (data == AFL_OP_OK) { + return 0; + } /*OP_OK? */ + if (data == AFL_OP_ERR) { + STEN_LOW; + data = inb(DATA_PORT); + printk + ("### Error 1 aztcd: aztSendCmd %x Error Code %x\n", + cmd, data); + } + } + if (retry >= AZT_RETRY_ATTEMPTS) { + printk("### Error 2 aztcd: aztSendCmd %x \n", cmd); + azt_error = 0xA5; + } + RETURNM("aztSendCmd", -1); } /* * Send a play or read command to the drive, return -1 on error, else 0 */ static int sendAztCmd(int cmd, struct azt_Play_msf *params) -{ unsigned char data; - int retry; +{ + unsigned char data; + int retry; #ifdef AZT_DEBUG - printk("aztcd: play start=%02x:%02x:%02x end=%02x:%02x:%02x\n", \ - params->start.min, params->start.sec, params->start.frame, \ - params->end.min, params->end.sec, params->end.frame); -#endif - for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++) - { aztSendCmd(cmd); - outb(params -> start.min,CMD_PORT); - outb(params -> start.sec,CMD_PORT); - outb(params -> start.frame,CMD_PORT); - outb(params -> end.min,CMD_PORT); - outb(params -> end.sec,CMD_PORT); - outb(params -> end.frame,CMD_PORT); - STEN_LOW; - data=inb(DATA_PORT); - if (data==AFL_PA_OK) - { return 0;} /*PA_OK ?*/ - if (data==AFL_PA_ERR) - { STEN_LOW; - data=inb(DATA_PORT); - printk("### Error 1 aztcd: sendAztCmd %x Error Code %x\n",cmd,data); - } - } - if (retry>=AZT_RETRY_ATTEMPTS) - { printk("### Error 2 aztcd: sendAztCmd %x\n ",cmd); - azt_error=0xA5; - } - RETURNM("sendAztCmd",-1); + printk("aztcd: play start=%02x:%02x:%02x end=%02x:%02x:%02x\n", + params->start.min, params->start.sec, params->start.frame, + params->end.min, params->end.sec, params->end.frame); +#endif + for (retry = 0; retry < AZT_RETRY_ATTEMPTS; retry++) { + aztSendCmd(cmd); + outb(params->start.min, CMD_PORT); + outb(params->start.sec, CMD_PORT); + outb(params->start.frame, CMD_PORT); + outb(params->end.min, CMD_PORT); + outb(params->end.sec, CMD_PORT); + outb(params->end.frame, CMD_PORT); + STEN_LOW; + data = inb(DATA_PORT); + if (data == AFL_PA_OK) { + return 0; + } /*PA_OK ? */ + if (data == AFL_PA_ERR) { + STEN_LOW; + data = inb(DATA_PORT); + printk + ("### Error 1 aztcd: sendAztCmd %x Error Code %x\n", + cmd, data); + } + } + if (retry >= AZT_RETRY_ATTEMPTS) { + printk("### Error 2 aztcd: sendAztCmd %x\n ", cmd); + azt_error = 0xA5; + } + RETURNM("sendAztCmd", -1); } /* * Send a seek command to the drive, return -1 on error, else 0 */ static int aztSeek(struct azt_Play_msf *params) -{ unsigned char data; - int retry; +{ + unsigned char data; + int retry; #ifdef AZT_DEBUG - printk("aztcd: aztSeek %02x:%02x:%02x\n", \ - params->start.min, params->start.sec, params->start.frame); -#endif - for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++) - { aztSendCmd(ACMD_SEEK); - outb(params -> start.min,CMD_PORT); - outb(params -> start.sec,CMD_PORT); - outb(params -> start.frame,CMD_PORT); - STEN_LOW; - data=inb(DATA_PORT); - if (data==AFL_PA_OK) - { return 0;} /*PA_OK ?*/ - if (data==AFL_PA_ERR) - { STEN_LOW; - data=inb(DATA_PORT); - printk("### Error 1 aztcd: aztSeek\n"); - } - } - if (retry>=AZT_RETRY_ATTEMPTS) - { printk("### Error 2 aztcd: aztSeek\n "); - azt_error=0xA5; - } - RETURNM("aztSeek",-1); + printk("aztcd: aztSeek %02x:%02x:%02x\n", + params->start.min, params->start.sec, params->start.frame); +#endif + for (retry = 0; retry < AZT_RETRY_ATTEMPTS; retry++) { + aztSendCmd(ACMD_SEEK); + outb(params->start.min, CMD_PORT); + outb(params->start.sec, CMD_PORT); + outb(params->start.frame, CMD_PORT); + STEN_LOW; + data = inb(DATA_PORT); + if (data == AFL_PA_OK) { + return 0; + } /*PA_OK ? */ + if (data == AFL_PA_ERR) { + STEN_LOW; + data = inb(DATA_PORT); + printk("### Error 1 aztcd: aztSeek\n"); + } + } + if (retry >= AZT_RETRY_ATTEMPTS) { + printk("### Error 2 aztcd: aztSeek\n "); + azt_error = 0xA5; + } + RETURNM("aztSeek", -1); } /* Send a Set Disk Type command @@ -576,79 +604,86 @@ static int aztSeek(struct azt_Play_msf *params) dent on which mode is set ??? */ static int aztSetDiskType(int type) -{ unsigned char data; - int retry; +{ + unsigned char data; + int retry; #ifdef AZT_DEBUG - printk("aztcd: set disk type command: type= %i\n",type); + printk("aztcd: set disk type command: type= %i\n", type); #endif - for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++) - { aztSendCmd(ACMD_SET_DISK_TYPE); - outb(type,CMD_PORT); - STEN_LOW; - data=inb(DATA_PORT); - if (data==AFL_PA_OK) /*PA_OK ?*/ - { azt_read_mode=type; - return 0; - } - if (data==AFL_PA_ERR) - { STEN_LOW; - data=inb(DATA_PORT); - printk("### Error 1 aztcd: aztSetDiskType %x Error Code %x\n",type,data); - } - } - if (retry>=AZT_RETRY_ATTEMPTS) - { printk("### Error 2 aztcd: aztSetDiskType %x\n ",type); - azt_error=0xA5; - } - RETURNM("aztSetDiskType",-1); + for (retry = 0; retry < AZT_RETRY_ATTEMPTS; retry++) { + aztSendCmd(ACMD_SET_DISK_TYPE); + outb(type, CMD_PORT); + STEN_LOW; + data = inb(DATA_PORT); + if (data == AFL_PA_OK) { /*PA_OK ? */ + azt_read_mode = type; + return 0; + } + if (data == AFL_PA_ERR) { + STEN_LOW; + data = inb(DATA_PORT); + printk + ("### Error 1 aztcd: aztSetDiskType %x Error Code %x\n", + type, data); + } + } + if (retry >= AZT_RETRY_ATTEMPTS) { + printk("### Error 2 aztcd: aztSetDiskType %x\n ", type); + azt_error = 0xA5; + } + RETURNM("aztSetDiskType", -1); } /* used in azt_poll to poll the status, expects another program to issue a * ACMD_GET_STATUS directly before */ -static int aztStatus(void) -{ int st; +static int aztStatus(void) +{ + int st; /* int i; i = inb(STATUS_PORT) & AFL_STATUS; is STEN=0? ??? if (!i) -*/ STEN_LOW; - if (aztTimeOutCount<AZT_TIMEOUT) - { st = inb(DATA_PORT) & 0xFF; +*/ STEN_LOW; + if (aztTimeOutCount < AZT_TIMEOUT) { + st = inb(DATA_PORT) & 0xFF; return st; - } - else - RETURNM("aztStatus",-1); + } else + RETURNM("aztStatus", -1); } /* * Get the drive status */ static int getAztStatus(void) -{ int st; +{ + int st; - if (aztSendCmd(ACMD_GET_STATUS)) RETURNM("getAztStatus 1",-1); + if (aztSendCmd(ACMD_GET_STATUS)) + RETURNM("getAztStatus 1", -1); STEN_LOW; st = inb(DATA_PORT) & 0xFF; #ifdef AZT_DEBUG - printk("aztcd: Status = %x\n",st); + printk("aztcd: Status = %x\n", st); #endif - if ((st == 0xFF)||(st&AST_CMD_CHECK)) - { printk("aztcd: AST_CMD_CHECK error or no status available\n"); - return -1; - } - - if (((st&AST_MODE_BITS)!=AST_BUSY) && (aztAudioStatus == CDROM_AUDIO_PLAY)) - /* XXX might be an error? look at q-channel? */ - aztAudioStatus = CDROM_AUDIO_COMPLETED; - - if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) - { aztDiskChanged = 1; - aztTocUpToDate = 0; - aztAudioStatus = CDROM_AUDIO_NO_STATUS; - } + if ((st == 0xFF) || (st & AST_CMD_CHECK)) { + printk + ("aztcd: AST_CMD_CHECK error or no status available\n"); + return -1; + } + + if (((st & AST_MODE_BITS) != AST_BUSY) + && (aztAudioStatus == CDROM_AUDIO_PLAY)) + /* XXX might be an error? look at q-channel? */ + aztAudioStatus = CDROM_AUDIO_COMPLETED; + + if ((st & AST_DSK_CHG) || (st & AST_NOT_READY)) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + aztAudioStatus = CDROM_AUDIO_NO_STATUS; + } return st; } @@ -657,7 +692,9 @@ static int getAztStatus(void) * Send a 'Play' command and get the status. Use only from the top half. */ static int aztPlay(struct azt_Play_msf *arg) -{ if (sendAztCmd(ACMD_PLAY_AUDIO, arg) < 0) RETURNM("aztPlay",-1); +{ + if (sendAztCmd(ACMD_PLAY_AUDIO, arg) < 0) + RETURNM("aztPlay", -1); return 0; } @@ -668,27 +705,27 @@ static int aztPlay(struct azt_Play_msf *arg) */ static void aztCloseDoor(void) { - aztSendCmd(ACMD_CLOSE); - STEN_LOW; - return; + aztSendCmd(ACMD_CLOSE); + STEN_LOW; + return; } static void aztLockDoor(void) { #if AZT_ALLOW_TRAY_LOCK - aztSendCmd(ACMD_LOCK); - STEN_LOW; + aztSendCmd(ACMD_LOCK); + STEN_LOW; #endif - return; + return; } static void aztUnlockDoor(void) { #if AZT_ALLOW_TRAY_LOCK - aztSendCmd(ACMD_UNLOCK); - STEN_LOW; + aztSendCmd(ACMD_UNLOCK); + STEN_LOW; #endif - return; + return; } /* @@ -697,11 +734,12 @@ static void aztUnlockDoor(void) * be issued with aztSendCmd() directly before */ static int aztGetValue(unsigned char *result) -{ int s; +{ + int s; STEN_LOW; - if (aztTimeOutCount>=AZT_TIMEOUT) - { printk("aztcd: aztGetValue timeout\n"); + if (aztTimeOutCount >= AZT_TIMEOUT) { + printk("aztcd: aztGetValue timeout\n"); return -1; } s = inb(DATA_PORT) & 0xFF; @@ -714,42 +752,55 @@ static int aztGetValue(unsigned char *result) * table of contents. */ int aztGetQChannelInfo(struct azt_Toc *qp) -{ unsigned char notUsed; +{ + unsigned char notUsed; int st; #ifdef AZT_DEBUG - printk("aztcd: starting aztGetQChannelInfo Time:%li\n",jiffies); + printk("aztcd: starting aztGetQChannelInfo Time:%li\n", jiffies); #endif - if ((st=getAztStatus())==-1) RETURNM("aztGetQChannelInfo 1",-1); - if (aztSendCmd(ACMD_GET_Q_CHANNEL)) RETURNM("aztGetQChannelInfo 2",-1); - /*STEN_LOW_WAIT; ??? Dosemu0.60's cdrom.c does not like STEN_LOW_WAIT here*/ - if (aztGetValue(¬Used)) RETURNM("aztGetQChannelInfo 3",-1); /*??? Nullbyte einlesen*/ - if ((st&AST_MODE_BITS)==AST_INITIAL) - { qp->ctrl_addr=0; /* when audio stop ACMD_GET_Q_CHANNEL returns */ - qp->track=0; /* only one byte with Aztech drives */ - qp->pointIndex=0; - qp->trackTime.min=0; - qp->trackTime.sec=0; - qp->trackTime.frame=0; - qp->diskTime.min=0; - qp->diskTime.sec=0; - qp->diskTime.frame=0; - return 0; - } - else - { if (aztGetValue(&qp -> ctrl_addr) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(&qp -> track) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(&qp -> pointIndex) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(&qp -> trackTime.min) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(&qp -> trackTime.sec) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(&qp -> trackTime.frame) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(¬Used) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(&qp -> diskTime.min) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(&qp -> diskTime.sec) < 0) RETURNM("aztGetQChannelInfo 4",-1); - if (aztGetValue(&qp -> diskTime.frame) < 0) RETURNM("aztGetQChannelInfo 4",-1); - } + if ((st = getAztStatus()) == -1) + RETURNM("aztGetQChannelInfo 1", -1); + if (aztSendCmd(ACMD_GET_Q_CHANNEL)) + RETURNM("aztGetQChannelInfo 2", -1); + /*STEN_LOW_WAIT; ??? Dosemu0.60's cdrom.c does not like STEN_LOW_WAIT here */ + if (aztGetValue(¬Used)) + RETURNM("aztGetQChannelInfo 3", -1); /*??? Nullbyte einlesen */ + if ((st & AST_MODE_BITS) == AST_INITIAL) { + qp->ctrl_addr = 0; /* when audio stop ACMD_GET_Q_CHANNEL returns */ + qp->track = 0; /* only one byte with Aztech drives */ + qp->pointIndex = 0; + qp->trackTime.min = 0; + qp->trackTime.sec = 0; + qp->trackTime.frame = 0; + qp->diskTime.min = 0; + qp->diskTime.sec = 0; + qp->diskTime.frame = 0; + return 0; + } else { + if (aztGetValue(&qp->ctrl_addr) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(&qp->track) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(&qp->pointIndex) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(&qp->trackTime.min) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(&qp->trackTime.sec) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(&qp->trackTime.frame) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(¬Used) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(&qp->diskTime.min) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(&qp->diskTime.sec) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + if (aztGetValue(&qp->diskTime.frame) < 0) + RETURNM("aztGetQChannelInfo 4", -1); + } #ifdef AZT_DEBUG - printk("aztcd: exiting aztGetQChannelInfo Time:%li\n",jiffies); + printk("aztcd: exiting aztGetQChannelInfo Time:%li\n", jiffies); #endif return 0; } @@ -758,11 +809,12 @@ int aztGetQChannelInfo(struct azt_Toc *qp) * Read the table of contents (TOC) and TOC header if necessary */ static int aztUpdateToc() -{ int st; +{ + int st; #ifdef AZT_DEBUG - printk("aztcd: starting aztUpdateToc Time:%li\n",jiffies); -#endif + printk("aztcd: starting aztUpdateToc Time:%li\n", jiffies); +#endif if (aztTocUpToDate) return 0; @@ -772,57 +824,63 @@ static int aztUpdateToc() if (aztGetToc(0) < 0) return -EIO; - /*audio disk detection - with my Aztech drive there is no audio status bit, so I use the copy - protection bit of the first track. If this track is copy protected - (copy bit = 0), I assume, it's an audio disk. Strange, but works ??? */ - if (!(Toc[DiskInfo.first].ctrl_addr & 0x40)) - DiskInfo.audio=1; - else - DiskInfo.audio=0; - - /* XA detection */ - if (! DiskInfo.audio) - { azt_Play.start.min = 0; /*XA detection only seems to work*/ - azt_Play.start.sec = 2; /*when we play a track*/ - azt_Play.start.frame = 0; - azt_Play.end.min = 0; - azt_Play.end.sec = 0; - azt_Play.end.frame = 1; - if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1; - DTEN_LOW; - for (st=0;st<CD_FRAMESIZE;st++) inb(DATA_PORT); - } - DiskInfo.xa = getAztStatus() & AST_MODE; - if (DiskInfo.xa) - { printk("aztcd: XA support experimental - mail results to Werner.Zimmermann@fht-esslingen.de\n"); - } - - /*multisession detection - support for multisession CDs is done automatically with Aztech drives, - we don't have to take care about TOC redirection; if we want the isofs - to take care about redirection, we have to set AZT_MULTISESSION to 1*/ - DiskInfo.multi=0; + /*audio disk detection + with my Aztech drive there is no audio status bit, so I use the copy + protection bit of the first track. If this track is copy protected + (copy bit = 0), I assume, it's an audio disk. Strange, but works ??? */ + if (!(Toc[DiskInfo.first].ctrl_addr & 0x40)) + DiskInfo.audio = 1; + else + DiskInfo.audio = 0; + + /* XA detection */ + if (!DiskInfo.audio) { + azt_Play.start.min = 0; /*XA detection only seems to work */ + azt_Play.start.sec = 2; /*when we play a track */ + azt_Play.start.frame = 0; + azt_Play.end.min = 0; + azt_Play.end.sec = 0; + azt_Play.end.frame = 1; + if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) + return -1; + DTEN_LOW; + for (st = 0; st < CD_FRAMESIZE; st++) + inb(DATA_PORT); + } + DiskInfo.xa = getAztStatus() & AST_MODE; + if (DiskInfo.xa) { + printk + ("aztcd: XA support experimental - mail results to Werner.Zimmermann@fht-esslingen.de\n"); + } + + /*multisession detection + support for multisession CDs is done automatically with Aztech drives, + we don't have to take care about TOC redirection; if we want the isofs + to take care about redirection, we have to set AZT_MULTISESSION to 1 */ + DiskInfo.multi = 0; #if AZT_MULTISESSION - if (DiskInfo.xa) - { aztGetMultiDiskInfo(); /*here Disk.Info.multi is set*/ - } + if (DiskInfo.xa) { + aztGetMultiDiskInfo(); /*here Disk.Info.multi is set */ + } #endif - if (DiskInfo.multi) - { DiskInfo.lastSession.min = Toc[DiskInfo.next].diskTime.min; - DiskInfo.lastSession.sec = Toc[DiskInfo.next].diskTime.sec; - DiskInfo.lastSession.frame= Toc[DiskInfo.next].diskTime.frame; - printk("aztcd: Multisession support experimental\n"); - } - else - { DiskInfo.lastSession.min = Toc[DiskInfo.first].diskTime.min; - DiskInfo.lastSession.sec = Toc[DiskInfo.first].diskTime.sec; - DiskInfo.lastSession.frame= Toc[DiskInfo.first].diskTime.frame; - } + if (DiskInfo.multi) { + DiskInfo.lastSession.min = Toc[DiskInfo.next].diskTime.min; + DiskInfo.lastSession.sec = Toc[DiskInfo.next].diskTime.sec; + DiskInfo.lastSession.frame = + Toc[DiskInfo.next].diskTime.frame; + printk("aztcd: Multisession support experimental\n"); + } else { + DiskInfo.lastSession.min = + Toc[DiskInfo.first].diskTime.min; + DiskInfo.lastSession.sec = + Toc[DiskInfo.first].diskTime.sec; + DiskInfo.lastSession.frame = + Toc[DiskInfo.first].diskTime.frame; + } aztTocUpToDate = 1; #ifdef AZT_DEBUG - printk("aztcd: exiting aztUpdateToc Time:%li\n",jiffies); + printk("aztcd: exiting aztUpdateToc Time:%li\n", jiffies); #endif return 0; } @@ -832,56 +890,58 @@ static int aztUpdateToc() * track */ static int aztGetDiskInfo() -{ int limit; - unsigned char test; - struct azt_Toc qInfo; +{ + int limit; + unsigned char test; + struct azt_Toc qInfo; #ifdef AZT_DEBUG - printk("aztcd: starting aztGetDiskInfo Time:%li\n",jiffies); + printk("aztcd: starting aztGetDiskInfo Time:%li\n", jiffies); #endif - if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetDiskInfo 1",-1); - STEN_LOW_WAIT; - test=0; - for (limit=300;limit>0;limit--) - { if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetDiskInfo 2",-1); - if (qInfo.pointIndex==0xA0) /*Number of FirstTrack*/ - { DiskInfo.first = qInfo.diskTime.min; - DiskInfo.first = azt_bcd2bin(DiskInfo.first); - test=test|0x01; - } - if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/ - { DiskInfo.last = qInfo.diskTime.min; - DiskInfo.last = azt_bcd2bin(DiskInfo.last); - test=test|0x02; - } - if (qInfo.pointIndex==0xA2) /*DiskLength*/ - { DiskInfo.diskLength.min=qInfo.diskTime.min; - DiskInfo.diskLength.sec=qInfo.diskTime.sec; - DiskInfo.diskLength.frame=qInfo.diskTime.frame; - test=test|0x04; - } - if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01)) /*StartTime of First Track*/ - { DiskInfo.firstTrack.min=qInfo.diskTime.min; - DiskInfo.firstTrack.sec=qInfo.diskTime.sec; - DiskInfo.firstTrack.frame=qInfo.diskTime.frame; - test=test|0x08; + if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) + RETURNM("aztGetDiskInfo 1", -1); + STEN_LOW_WAIT; + test = 0; + for (limit = 300; limit > 0; limit--) { + if (aztGetQChannelInfo(&qInfo) < 0) + RETURNM("aztGetDiskInfo 2", -1); + if (qInfo.pointIndex == 0xA0) { /*Number of FirstTrack */ + DiskInfo.first = qInfo.diskTime.min; + DiskInfo.first = azt_bcd2bin(DiskInfo.first); + test = test | 0x01; + } + if (qInfo.pointIndex == 0xA1) { /*Number of LastTrack */ + DiskInfo.last = qInfo.diskTime.min; + DiskInfo.last = azt_bcd2bin(DiskInfo.last); + test = test | 0x02; + } + if (qInfo.pointIndex == 0xA2) { /*DiskLength */ + DiskInfo.diskLength.min = qInfo.diskTime.min; + DiskInfo.diskLength.sec = qInfo.diskTime.sec; + DiskInfo.diskLength.frame = qInfo.diskTime.frame; + test = test | 0x04; + } + if ((qInfo.pointIndex == DiskInfo.first) && (test & 0x01)) { /*StartTime of First Track */ + DiskInfo.firstTrack.min = qInfo.diskTime.min; + DiskInfo.firstTrack.sec = qInfo.diskTime.sec; + DiskInfo.firstTrack.frame = qInfo.diskTime.frame; + test = test | 0x08; + } + if (test == 0x0F) + break; } - if (test==0x0F) break; - } #ifdef AZT_DEBUG - printk ("aztcd: exiting aztGetDiskInfo Time:%li\n",jiffies); - printk("Disk Info: first %d last %d length %02X:%02X.%02X dez first %02X:%02X.%02X dez\n", - DiskInfo.first, - DiskInfo.last, - DiskInfo.diskLength.min, - DiskInfo.diskLength.sec, - DiskInfo.diskLength.frame, - DiskInfo.firstTrack.min, - DiskInfo.firstTrack.sec, - DiskInfo.firstTrack.frame); + printk("aztcd: exiting aztGetDiskInfo Time:%li\n", jiffies); + printk + ("Disk Info: first %d last %d length %02X:%02X.%02X dez first %02X:%02X.%02X dez\n", + DiskInfo.first, DiskInfo.last, DiskInfo.diskLength.min, + DiskInfo.diskLength.sec, DiskInfo.diskLength.frame, + DiskInfo.firstTrack.min, DiskInfo.firstTrack.sec, + DiskInfo.firstTrack.frame); #endif - if (test!=0x0F) return -1; - return 0; + if (test != 0x0F) + return -1; + return 0; } #if AZT_MULTISESSION @@ -889,93 +949,105 @@ static int aztGetDiskInfo() * Get Multisession Disk Info */ static int aztGetMultiDiskInfo(void) -{ int limit, k=5; - unsigned char test; - struct azt_Toc qInfo; +{ + int limit, k = 5; + unsigned char test; + struct azt_Toc qInfo; #ifdef AZT_DEBUG - printk("aztcd: starting aztGetMultiDiskInfo\n"); + printk("aztcd: starting aztGetMultiDiskInfo\n"); #endif - do { azt_Play.start.min = Toc[DiskInfo.last+1].diskTime.min; - azt_Play.start.sec = Toc[DiskInfo.last+1].diskTime.sec; - azt_Play.start.frame = Toc[DiskInfo.last+1].diskTime.frame; - test=0; - - for (limit=30;limit>0;limit--) /*Seek for LeadIn of next session*/ - { if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 1",-1); - if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 2",-1); - if ((qInfo.track==0)&&(qInfo.pointIndex)) break; /*LeadIn found*/ - if ((azt_Play.start.sec+=10) > 59) - { azt_Play.start.sec=0; - azt_Play.start.min++; - } - } - if (!limit) break; /*Check, if a leadin track was found, if not we're - at the end of the disk*/ + do { + azt_Play.start.min = Toc[DiskInfo.last + 1].diskTime.min; + azt_Play.start.sec = Toc[DiskInfo.last + 1].diskTime.sec; + azt_Play.start.frame = + Toc[DiskInfo.last + 1].diskTime.frame; + test = 0; + + for (limit = 30; limit > 0; limit--) { /*Seek for LeadIn of next session */ + if (aztSeek(&azt_Play)) + RETURNM("aztGetMultiDiskInfo 1", -1); + if (aztGetQChannelInfo(&qInfo) < 0) + RETURNM("aztGetMultiDiskInfo 2", -1); + if ((qInfo.track == 0) && (qInfo.pointIndex)) + break; /*LeadIn found */ + if ((azt_Play.start.sec += 10) > 59) { + azt_Play.start.sec = 0; + azt_Play.start.min++; + } + } + if (!limit) + break; /*Check, if a leadin track was found, if not we're + at the end of the disk */ #ifdef AZT_DEBUG_MULTISESSION - printk("leadin found track %d pointIndex %x limit %d\n",qInfo.track,qInfo.pointIndex,limit); + printk("leadin found track %d pointIndex %x limit %d\n", + qInfo.track, qInfo.pointIndex, limit); #endif - for (limit=300;limit>0;limit--) - { if (++azt_Play.start.frame>74) - { azt_Play.start.frame=0; - if (azt_Play.start.sec > 59) - { azt_Play.start.sec=0; - azt_Play.start.min++; - } - } - if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 3",-1); - if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 4",-1); - if (qInfo.pointIndex==0xA0) /*Number of NextTrack*/ - { DiskInfo.next = qInfo.diskTime.min; - DiskInfo.next = azt_bcd2bin(DiskInfo.next); - test=test|0x01; - } - if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/ - { DiskInfo.last = qInfo.diskTime.min; - DiskInfo.last = azt_bcd2bin(DiskInfo.last); - test=test|0x02; - } - if (qInfo.pointIndex==0xA2) /*DiskLength*/ - { DiskInfo.diskLength.min =qInfo.diskTime.min; - DiskInfo.diskLength.sec =qInfo.diskTime.sec; - DiskInfo.diskLength.frame=qInfo.diskTime.frame; - test=test|0x04; - } - if ((qInfo.pointIndex==DiskInfo.next)&&(test&0x01)) /*StartTime of Next Track*/ - { DiskInfo.nextSession.min=qInfo.diskTime.min; - DiskInfo.nextSession.sec=qInfo.diskTime.sec; - DiskInfo.nextSession.frame=qInfo.diskTime.frame; - test=test|0x08; + for (limit = 300; limit > 0; limit--) { + if (++azt_Play.start.frame > 74) { + azt_Play.start.frame = 0; + if (azt_Play.start.sec > 59) { + azt_Play.start.sec = 0; + azt_Play.start.min++; + } + } + if (aztSeek(&azt_Play)) + RETURNM("aztGetMultiDiskInfo 3", -1); + if (aztGetQChannelInfo(&qInfo) < 0) + RETURNM("aztGetMultiDiskInfo 4", -1); + if (qInfo.pointIndex == 0xA0) { /*Number of NextTrack */ + DiskInfo.next = qInfo.diskTime.min; + DiskInfo.next = azt_bcd2bin(DiskInfo.next); + test = test | 0x01; + } + if (qInfo.pointIndex == 0xA1) { /*Number of LastTrack */ + DiskInfo.last = qInfo.diskTime.min; + DiskInfo.last = azt_bcd2bin(DiskInfo.last); + test = test | 0x02; + } + if (qInfo.pointIndex == 0xA2) { /*DiskLength */ + DiskInfo.diskLength.min = + qInfo.diskTime.min; + DiskInfo.diskLength.sec = + qInfo.diskTime.sec; + DiskInfo.diskLength.frame = + qInfo.diskTime.frame; + test = test | 0x04; + } + if ((qInfo.pointIndex == DiskInfo.next) && (test & 0x01)) { /*StartTime of Next Track */ + DiskInfo.nextSession.min = + qInfo.diskTime.min; + DiskInfo.nextSession.sec = + qInfo.diskTime.sec; + DiskInfo.nextSession.frame = + qInfo.diskTime.frame; + test = test | 0x08; + } + if (test == 0x0F) + break; } - if (test==0x0F) break; - } #ifdef AZT_DEBUG_MULTISESSION - printk ("MultiDisk Info: first %d next %d last %d length %02x:%02x.%02x dez first %02x:%02x.%02x dez next %02x:%02x.%02x dez\n", - DiskInfo.first, - DiskInfo.next, - DiskInfo.last, - DiskInfo.diskLength.min, - DiskInfo.diskLength.sec, - DiskInfo.diskLength.frame, - DiskInfo.firstTrack.min, - DiskInfo.firstTrack.sec, - DiskInfo.firstTrack.frame, - DiskInfo.nextSession.min, - DiskInfo.nextSession.sec, - DiskInfo.nextSession.frame); + printk + ("MultiDisk Info: first %d next %d last %d length %02x:%02x.%02x dez first %02x:%02x.%02x dez next %02x:%02x.%02x dez\n", + DiskInfo.first, DiskInfo.next, DiskInfo.last, + DiskInfo.diskLength.min, DiskInfo.diskLength.sec, + DiskInfo.diskLength.frame, DiskInfo.firstTrack.min, + DiskInfo.firstTrack.sec, DiskInfo.firstTrack.frame, + DiskInfo.nextSession.min, DiskInfo.nextSession.sec, + DiskInfo.nextSession.frame); #endif - if (test!=0x0F) - break; - else - DiskInfo.multi=1; /*found TOC of more than one session*/ - aztGetToc(1); - } while(--k); + if (test != 0x0F) + break; + else + DiskInfo.multi = 1; /*found TOC of more than one session */ + aztGetToc(1); + } while (--k); #ifdef AZT_DEBUG - printk ("aztcd: exiting aztGetMultiDiskInfo Time:%li\n",jiffies); + printk("aztcd: exiting aztGetMultiDiskInfo Time:%li\n", jiffies); #endif - return 0; + return 0; } #endif @@ -983,75 +1055,81 @@ static int aztGetMultiDiskInfo(void) * Read the table of contents (TOC) */ static int aztGetToc(int multi) -{ int i, px; - int limit; - struct azt_Toc qInfo; +{ + int i, px; + int limit; + struct azt_Toc qInfo; #ifdef AZT_DEBUG - printk("aztcd: starting aztGetToc Time:%li\n",jiffies); + printk("aztcd: starting aztGetToc Time:%li\n", jiffies); #endif - if (!multi) - { for (i = 0; i < MAX_TRACKS; i++) - Toc[i].pointIndex = 0; - i = DiskInfo.last + 3; - } - else - { for (i = DiskInfo.next; i < MAX_TRACKS; i++) - Toc[i].pointIndex = 0; - i = DiskInfo.last + 4 - DiskInfo.next; - } + if (!multi) { + for (i = 0; i < MAX_TRACKS; i++) + Toc[i].pointIndex = 0; + i = DiskInfo.last + 3; + } else { + for (i = DiskInfo.next; i < MAX_TRACKS; i++) + Toc[i].pointIndex = 0; + i = DiskInfo.last + 4 - DiskInfo.next; + } /*Is there a good reason to stop motor before TOC read? if (aztSendCmd(ACMD_STOP)) RETURNM("aztGetToc 1",-1); STEN_LOW_WAIT; */ - if (!multi) - { azt_mode = 0x05; - if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1); - STEN_LOW_WAIT; - } - for (limit = 300; limit > 0; limit--) - { if (multi) - { if (++azt_Play.start.sec > 59) - { azt_Play.start.sec=0; - azt_Play.start.min++; - } - if (aztSeek(&azt_Play)) RETURNM("aztGetToc 3",-1); - } - if (aztGetQChannelInfo(&qInfo) < 0) - break; - - px = azt_bcd2bin(qInfo.pointIndex); - - if (px > 0 && px < MAX_TRACKS && qInfo.track == 0) - if (Toc[px].pointIndex == 0) - { Toc[px] = qInfo; - i--; - } - - if (i <= 0) - break; - } - - Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength; - Toc[DiskInfo.last].trackTime = DiskInfo.diskLength; - -#ifdef AZT_DEBUG_MULTISESSION - printk("aztcd: exiting aztGetToc\n"); - for (i = 1; i <= DiskInfo.last+1; i++) - printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez %02X:%02X.%02X dez\n", - i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, - Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, - Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); - for (i = 100; i < 103; i++) - printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez %02X:%02X.%02X dez\n", - i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, - Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, - Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); + if (!multi) { + azt_mode = 0x05; + if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) + RETURNM("aztGetToc 2", -1); + STEN_LOW_WAIT; + } + for (limit = 300; limit > 0; limit--) { + if (multi) { + if (++azt_Play.start.sec > 59) { + azt_Play.start.sec = 0; + azt_Play.start.min++; + } + if (aztSeek(&azt_Play)) + RETURNM("aztGetToc 3", -1); + } + if (aztGetQChannelInfo(&qInfo) < 0) + break; + + px = azt_bcd2bin(qInfo.pointIndex); + + if (px > 0 && px < MAX_TRACKS && qInfo.track == 0) + if (Toc[px].pointIndex == 0) { + Toc[px] = qInfo; + i--; + } + + if (i <= 0) + break; + } + + Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength; + Toc[DiskInfo.last].trackTime = DiskInfo.diskLength; + +#ifdef AZT_DEBUG_MULTISESSION + printk("aztcd: exiting aztGetToc\n"); + for (i = 1; i <= DiskInfo.last + 1; i++) + printk + ("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez %02X:%02X.%02X dez\n", + i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, + Toc[i].trackTime.min, Toc[i].trackTime.sec, + Toc[i].trackTime.frame, Toc[i].diskTime.min, + Toc[i].diskTime.sec, Toc[i].diskTime.frame); + for (i = 100; i < 103; i++) + printk + ("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez %02X:%02X.%02X dez\n", + i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, + Toc[i].trackTime.min, Toc[i].trackTime.sec, + Toc[i].trackTime.frame, Toc[i].diskTime.min, + Toc[i].diskTime.sec, Toc[i].diskTime.frame); #endif - return limit > 0 ? 0 : -1; + return limit > 0 ? 0 : -1; } @@ -1063,160 +1141,185 @@ static int aztGetToc(int multi) #ifndef MODULE static int __init aztcd_setup(char *str) { - int ints[4]; - - (void)get_options(str, ARRAY_SIZE(ints), ints); - - if (ints[0] > 0) - azt_port = ints[1]; - if (ints[1] > 1) - azt_cont = ints[2]; - return 1; + int ints[4]; + + (void) get_options(str, ARRAY_SIZE(ints), ints); + + if (ints[0] > 0) + azt_port = ints[1]; + if (ints[1] > 1) + azt_cont = ints[2]; + return 1; } __setup("aztcd=", aztcd_setup); -#endif /* !MODULE */ +#endif /* !MODULE */ /* * Checking if the media has been changed */ static int check_aztcd_media_change(kdev_t full_dev) -{ if (aztDiskChanged) /* disk changed */ - { aztDiskChanged=0; - return 1; - } - else - return 0; /* no change */ +{ + if (aztDiskChanged) { /* disk changed */ + aztDiskChanged = 0; + return 1; + } else + return 0; /* no change */ } /* * Kernel IO-controls */ -static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) -{ int i; +static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int i; struct azt_Toc qInfo; struct cdrom_ti ti; struct cdrom_tochdr tocHdr; struct cdrom_msf msf; struct cdrom_tocentry entry; - struct azt_Toc *tocPtr; + struct azt_Toc *tocPtr; struct cdrom_subchnl subchnl; - struct cdrom_volctrl volctrl; + struct cdrom_volctrl volctrl; #ifdef AZT_DEBUG - printk("aztcd: starting aztcd_ioctl - Command:%x Time: %li\n",cmd, jiffies); - printk("aztcd Status %x\n", getAztStatus()); + printk("aztcd: starting aztcd_ioctl - Command:%x Time: %li\n", + cmd, jiffies); + printk("aztcd Status %x\n", getAztStatus()); #endif - if (!ip) RETURNM("aztcd_ioctl 1",-EINVAL); - if (getAztStatus()<0) RETURNM("aztcd_ioctl 2", -EIO); - if ((!aztTocUpToDate)||(aztDiskChanged)) - { if ((i=aztUpdateToc())<0) RETURNM("aztcd_ioctl 3", i); /* error reading TOC */ + if (!ip) + RETURNM("aztcd_ioctl 1", -EINVAL); + if (getAztStatus() < 0) + RETURNM("aztcd_ioctl 2", -EIO); + if ((!aztTocUpToDate) || (aztDiskChanged)) { + if ((i = aztUpdateToc()) < 0) + RETURNM("aztcd_ioctl 3", i); /* error reading TOC */ } - switch (cmd) - { - case CDROMSTART: /* Spin up the drive. Don't know, what to do, - at least close the tray */ -#if AZT_PRIVATE_IOCTLS - if (aztSendCmd(ACMD_CLOSE)) RETURNM("aztcd_ioctl 4",-1); - STEN_LOW_WAIT; + switch (cmd) { + case CDROMSTART: /* Spin up the drive. Don't know, what to do, + at least close the tray */ +#if AZT_PRIVATE_IOCTLS + if (aztSendCmd(ACMD_CLOSE)) + RETURNM("aztcd_ioctl 4", -1); + STEN_LOW_WAIT; #endif break; - case CDROMSTOP: /* Spin down the drive */ - if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 5",-1); + case CDROMSTOP: /* Spin down the drive */ + if (aztSendCmd(ACMD_STOP)) + RETURNM("aztcd_ioctl 5", -1); STEN_LOW_WAIT; /* should we do anything if it fails? */ aztAudioStatus = CDROM_AUDIO_NO_STATUS; break; - case CDROMPAUSE: /* Pause the drive */ - if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL; + case CDROMPAUSE: /* Pause the drive */ + if (aztAudioStatus != CDROM_AUDIO_PLAY) + return -EINVAL; - if (aztGetQChannelInfo(&qInfo) < 0) - { /* didn't get q channel info */ - aztAudioStatus = CDROM_AUDIO_NO_STATUS; - RETURNM("aztcd_ioctl 7",0); + if (aztGetQChannelInfo(&qInfo) < 0) { /* didn't get q channel info */ + aztAudioStatus = CDROM_AUDIO_NO_STATUS; + RETURNM("aztcd_ioctl 7", 0); } - azt_Play.start = qInfo.diskTime; /* remember restart point */ + azt_Play.start = qInfo.diskTime; /* remember restart point */ - if (aztSendCmd(ACMD_PAUSE)) RETURNM("aztcd_ioctl 8",-1); + if (aztSendCmd(ACMD_PAUSE)) + RETURNM("aztcd_ioctl 8", -1); STEN_LOW_WAIT; aztAudioStatus = CDROM_AUDIO_PAUSED; break; - case CDROMRESUME: /* Play it again, Sam */ - if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL; + case CDROMRESUME: /* Play it again, Sam */ + if (aztAudioStatus != CDROM_AUDIO_PAUSED) + return -EINVAL; /* restart the drive at the saved position. */ i = aztPlay(&azt_Play); - if (i < 0) - { aztAudioStatus = CDROM_AUDIO_ERROR; - return -EIO; + if (i < 0) { + aztAudioStatus = CDROM_AUDIO_ERROR; + return -EIO; } aztAudioStatus = CDROM_AUDIO_PLAY; break; - case CDROMMULTISESSION: /*multisession support -- experimental*/ - { struct cdrom_multisession ms; + case CDROMMULTISESSION: /*multisession support -- experimental */ + { + struct cdrom_multisession ms; #ifdef AZT_DEBUG - printk("aztcd ioctl MULTISESSION\n"); + printk("aztcd ioctl MULTISESSION\n"); #endif - if(copy_from_user(&ms, (void*) arg, sizeof(struct cdrom_multisession))) - return -EFAULT; - if (ms.addr_format == CDROM_MSF) - { ms.addr.msf.minute = azt_bcd2bin(DiskInfo.lastSession.min); - ms.addr.msf.second = azt_bcd2bin(DiskInfo.lastSession.sec); - ms.addr.msf.frame = azt_bcd2bin(DiskInfo.lastSession.frame); - } - else if (ms.addr_format == CDROM_LBA) - ms.addr.lba = azt_msf2hsg(&DiskInfo.lastSession); - else - return -EINVAL; - ms.xa_flag = DiskInfo.xa; - if(copy_to_user((void*) arg, &ms, sizeof(struct cdrom_multisession))) - return -EFAULT; -#ifdef AZT_DEBUG - if (ms.addr_format == CDROM_MSF) - printk("aztcd multisession xa:%d, msf:%02x:%02x.%02x [%02x:%02x.%02x])\n", - ms.xa_flag, ms.addr.msf.minute, ms.addr.msf.second, - ms.addr.msf.frame, DiskInfo.lastSession.min, - DiskInfo.lastSession.sec, DiskInfo.lastSession.frame); - else - printk("aztcd multisession %d, lba:0x%08x [%02x:%02x.%02x])\n", - ms.xa_flag, ms.addr.lba, DiskInfo.lastSession.min, - DiskInfo.lastSession.sec, DiskInfo.lastSession.frame); + if (copy_from_user + (&ms, (void *) arg, + sizeof(struct cdrom_multisession))) + return -EFAULT; + if (ms.addr_format == CDROM_MSF) { + ms.addr.msf.minute = + azt_bcd2bin(DiskInfo.lastSession.min); + ms.addr.msf.second = + azt_bcd2bin(DiskInfo.lastSession.sec); + ms.addr.msf.frame = + azt_bcd2bin(DiskInfo.lastSession. + frame); + } else if (ms.addr_format == CDROM_LBA) + ms.addr.lba = + azt_msf2hsg(&DiskInfo.lastSession); + else + return -EINVAL; + ms.xa_flag = DiskInfo.xa; + if (copy_to_user + ((void *) arg, &ms, + sizeof(struct cdrom_multisession))) + return -EFAULT; +#ifdef AZT_DEBUG + if (ms.addr_format == CDROM_MSF) + printk + ("aztcd multisession xa:%d, msf:%02x:%02x.%02x [%02x:%02x.%02x])\n", + ms.xa_flag, ms.addr.msf.minute, + ms.addr.msf.second, ms.addr.msf.frame, + DiskInfo.lastSession.min, + DiskInfo.lastSession.sec, + DiskInfo.lastSession.frame); + else + printk + ("aztcd multisession %d, lba:0x%08x [%02x:%02x.%02x])\n", + ms.xa_flag, ms.addr.lba, + DiskInfo.lastSession.min, + DiskInfo.lastSession.sec, + DiskInfo.lastSession.frame); #endif - return 0; + return 0; } - case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - if(copy_from_user(&ti, (void *) arg, sizeof ti)) + case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ + if (copy_from_user(&ti, (void *) arg, sizeof ti)) return -EFAULT; if (ti.cdti_trk0 < DiskInfo.first - || ti.cdti_trk0 > DiskInfo.last - || ti.cdti_trk1 < ti.cdti_trk0) - { return -EINVAL; + || ti.cdti_trk0 > DiskInfo.last + || ti.cdti_trk1 < ti.cdti_trk0) { + return -EINVAL; } if (ti.cdti_trk1 > DiskInfo.last) - ti.cdti_trk1 = DiskInfo.last; + ti.cdti_trk1 = DiskInfo.last; azt_Play.start = Toc[ti.cdti_trk0].diskTime; azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime; #ifdef AZT_DEBUG -printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n", - azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame, - azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame); + printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n", + azt_Play.start.min, azt_Play.start.sec, + azt_Play.start.frame, azt_Play.end.min, + azt_Play.end.sec, azt_Play.end.frame); #endif i = aztPlay(&azt_Play); - if (i < 0) - { aztAudioStatus = CDROM_AUDIO_ERROR; - return -EIO; + if (i < 0) { + aztAudioStatus = CDROM_AUDIO_ERROR; + return -EIO; } aztAudioStatus = CDROM_AUDIO_PLAY; break; - case CDROMPLAYMSF: /* Play starting at the given MSF address. */ + case CDROMPLAYMSF: /* Play starting at the given MSF address. */ /* if (aztAudioStatus == CDROM_AUDIO_PLAY) { if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 9",-1); STEN_LOW; aztAudioStatus = CDROM_AUDIO_NO_STATUS; } */ - if(copy_from_user(&msf, (void *) arg, sizeof msf)) + if (copy_from_user(&msf, (void *) arg, sizeof msf)) return -EFAULT; /* convert to bcd */ azt_bin2bcd(&msf.cdmsf_min0); @@ -1232,163 +1335,189 @@ printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n", azt_Play.end.sec = msf.cdmsf_sec1; azt_Play.end.frame = msf.cdmsf_frame1; #ifdef AZT_DEBUG -printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n", -azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame, -azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame); + printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n", + azt_Play.start.min, azt_Play.start.sec, + azt_Play.start.frame, azt_Play.end.min, + azt_Play.end.sec, azt_Play.end.frame); #endif i = aztPlay(&azt_Play); - if (i < 0) - { aztAudioStatus = CDROM_AUDIO_ERROR; - return -EIO; + if (i < 0) { + aztAudioStatus = CDROM_AUDIO_ERROR; + return -EIO; } aztAudioStatus = CDROM_AUDIO_PLAY; break; - case CDROMREADTOCHDR: /* Read the table of contents header */ + case CDROMREADTOCHDR: /* Read the table of contents header */ tocHdr.cdth_trk0 = DiskInfo.first; tocHdr.cdth_trk1 = DiskInfo.last; - if(copy_to_user((void *) arg, &tocHdr, sizeof tocHdr)) + if (copy_to_user((void *) arg, &tocHdr, sizeof tocHdr)) return -EFAULT; break; - case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ - if(copy_from_user(&entry, (void *) arg, sizeof entry)) + case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ + if (copy_from_user(&entry, (void *) arg, sizeof entry)) return -EFAULT; - if ((!aztTocUpToDate)||aztDiskChanged) aztUpdateToc(); + if ((!aztTocUpToDate) || aztDiskChanged) + aztUpdateToc(); if (entry.cdte_track == CDROM_LEADOUT) - tocPtr = &Toc[DiskInfo.last + 1]; + tocPtr = &Toc[DiskInfo.last + 1]; else if (entry.cdte_track > DiskInfo.last - || entry.cdte_track < DiskInfo.first) - { return -EINVAL; - } - else - tocPtr = &Toc[entry.cdte_track]; - entry.cdte_adr = tocPtr -> ctrl_addr; - entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4; + || entry.cdte_track < DiskInfo.first) { + return -EINVAL; + } else + tocPtr = &Toc[entry.cdte_track]; + entry.cdte_adr = tocPtr->ctrl_addr; + entry.cdte_ctrl = tocPtr->ctrl_addr >> 4; if (entry.cdte_format == CDROM_LBA) - entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime); - else if (entry.cdte_format == CDROM_MSF) - { entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min); - entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec); - entry.cdte_addr.msf.frame = azt_bcd2bin(tocPtr -> diskTime.frame); + entry.cdte_addr.lba = + azt_msf2hsg(&tocPtr->diskTime); + else if (entry.cdte_format == CDROM_MSF) { + entry.cdte_addr.msf.minute = + azt_bcd2bin(tocPtr->diskTime.min); + entry.cdte_addr.msf.second = + azt_bcd2bin(tocPtr->diskTime.sec); + entry.cdte_addr.msf.frame = + azt_bcd2bin(tocPtr->diskTime.frame); + } else { + return -EINVAL; } - else - { return -EINVAL; - } - if(copy_to_user((void *) arg, &entry, sizeof entry)) + if (copy_to_user((void *) arg, &entry, sizeof entry)) return -EFAULT; break; - case CDROMSUBCHNL: /* Get subchannel info */ - if(copy_from_user(&subchnl, (void *) arg, sizeof (struct cdrom_subchnl))) + case CDROMSUBCHNL: /* Get subchannel info */ + if (copy_from_user + (&subchnl, (void *) arg, sizeof(struct cdrom_subchnl))) return -EFAULT; if (aztGetQChannelInfo(&qInfo) < 0) { #ifdef AZT_DEBUG - printk("aztcd: exiting aztcd_ioctl - Error 3 - Command:%x\n",cmd); + printk + ("aztcd: exiting aztcd_ioctl - Error 3 - Command:%x\n", + cmd); #endif - return -EIO; - } + return -EIO; + } subchnl.cdsc_audiostatus = aztAudioStatus; subchnl.cdsc_adr = qInfo.ctrl_addr; subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4; subchnl.cdsc_trk = azt_bcd2bin(qInfo.track); subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex); - if (subchnl.cdsc_format == CDROM_LBA) - { subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime); - subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime); - } - else /*default*/ - { subchnl.cdsc_format = CDROM_MSF; - subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min); - subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec); - subchnl.cdsc_absaddr.msf.frame = azt_bcd2bin(qInfo.diskTime.frame); - subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min); - subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec); - subchnl.cdsc_reladdr.msf.frame = azt_bcd2bin(qInfo.trackTime.frame); + if (subchnl.cdsc_format == CDROM_LBA) { + subchnl.cdsc_absaddr.lba = + azt_msf2hsg(&qInfo.diskTime); + subchnl.cdsc_reladdr.lba = + azt_msf2hsg(&qInfo.trackTime); + } else { /*default */ + subchnl.cdsc_format = CDROM_MSF; + subchnl.cdsc_absaddr.msf.minute = + azt_bcd2bin(qInfo.diskTime.min); + subchnl.cdsc_absaddr.msf.second = + azt_bcd2bin(qInfo.diskTime.sec); + subchnl.cdsc_absaddr.msf.frame = + azt_bcd2bin(qInfo.diskTime.frame); + subchnl.cdsc_reladdr.msf.minute = + azt_bcd2bin(qInfo.trackTime.min); + subchnl.cdsc_reladdr.msf.second = + azt_bcd2bin(qInfo.trackTime.sec); + subchnl.cdsc_reladdr.msf.frame = + azt_bcd2bin(qInfo.trackTime.frame); } - if(copy_to_user((void *) arg, &subchnl, sizeof (struct cdrom_subchnl))) + if (copy_to_user + ((void *) arg, &subchnl, sizeof(struct cdrom_subchnl))) return -EFAULT; break; - case CDROMVOLCTRL: /* Volume control - * With my Aztech CD268-01A volume control does not work, I can only - turn the channels on (any value !=0) or off (value==0). Maybe it - works better with your drive */ - if(copy_from_user(&volctrl,(char *) arg,sizeof(volctrl))) - return -EFAULT; + case CDROMVOLCTRL: /* Volume control + * With my Aztech CD268-01A volume control does not work, I can only + turn the channels on (any value !=0) or off (value==0). Maybe it + works better with your drive */ + if (copy_from_user + (&volctrl, (char *) arg, sizeof(volctrl))) + return -EFAULT; azt_Play.start.min = 0x21; azt_Play.start.sec = 0x84; azt_Play.start.frame = volctrl.channel0; - azt_Play.end.min = volctrl.channel1; - azt_Play.end.sec = volctrl.channel2; - azt_Play.end.frame = volctrl.channel3; - sendAztCmd(ACMD_SET_VOLUME, &azt_Play); - STEN_LOW_WAIT; - break; + azt_Play.end.min = volctrl.channel1; + azt_Play.end.sec = volctrl.channel2; + azt_Play.end.frame = volctrl.channel3; + sendAztCmd(ACMD_SET_VOLUME, &azt_Play); + STEN_LOW_WAIT; + break; case CDROMEJECT: - aztUnlockDoor(); /* Assume user knows what they're doing */ - /* all drives can at least stop! */ - if (aztAudioStatus == CDROM_AUDIO_PLAY) - { if (aztSendCmd(ACMD_STOP)) RETURNM("azt_ioctl 10",-1); - STEN_LOW_WAIT; + aztUnlockDoor(); /* Assume user knows what they're doing */ + /* all drives can at least stop! */ + if (aztAudioStatus == CDROM_AUDIO_PLAY) { + if (aztSendCmd(ACMD_STOP)) + RETURNM("azt_ioctl 10", -1); + STEN_LOW_WAIT; } - if (aztSendCmd(ACMD_EJECT)) RETURNM("azt_ioctl 11",-1); + if (aztSendCmd(ACMD_EJECT)) + RETURNM("azt_ioctl 11", -1); STEN_LOW_WAIT; aztAudioStatus = CDROM_AUDIO_NO_STATUS; break; case CDROMEJECT_SW: azt_auto_eject = (char) arg; - break; - case CDROMRESET: - outb(ACMD_SOFT_RESET,CMD_PORT); /*send reset*/ - STEN_LOW; - if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK?*/ - { printk("aztcd: AZTECH CD-ROM drive does not respond\n"); - } - break; + break; + case CDROMRESET: + outb(ACMD_SOFT_RESET, CMD_PORT); /*send reset */ + STEN_LOW; + if (inb(DATA_PORT) != AFL_OP_OK) { /*OP_OK? */ + printk + ("aztcd: AZTECH CD-ROM drive does not respond\n"); + } + break; /*Take care, the following code is not compatible with other CD-ROM drivers, use it at your own risk with cdplay.c. Set AZT_PRIVATE_IOCTLS to 0 in aztcd.h, if you do not want to use it! -*/ -#if AZT_PRIVATE_IOCTLS - case CDROMREADCOOKED: /*read data in mode 1 (2048 Bytes)*/ - case CDROMREADRAW: /*read data in mode 2 (2336 Bytes)*/ - { - if(copy_from_user(&msf, (void *) arg, sizeof msf)) - return -EFAULT; - /* convert to bcd */ - azt_bin2bcd(&msf.cdmsf_min0); - azt_bin2bcd(&msf.cdmsf_sec0); - azt_bin2bcd(&msf.cdmsf_frame0); - msf.cdmsf_min1=0; - msf.cdmsf_sec1=0; - msf.cdmsf_frame1=1; /*read only one frame*/ - azt_Play.start.min = msf.cdmsf_min0; - azt_Play.start.sec = msf.cdmsf_sec0; - azt_Play.start.frame = msf.cdmsf_frame0; - azt_Play.end.min = msf.cdmsf_min1; - azt_Play.end.sec = msf.cdmsf_sec1; - azt_Play.end.frame = msf.cdmsf_frame1; - if (cmd==CDROMREADRAW) - { if (DiskInfo.xa) - { return -1; /*XA Disks can't be read raw*/ - } - else - { if (sendAztCmd(ACMD_PLAY_READ_RAW, &azt_Play)) return -1; - DTEN_LOW; - insb(DATA_PORT,buf,CD_FRAMESIZE_RAW); - if(copy_to_user((void *) arg, &buf, CD_FRAMESIZE_RAW)) - return -EFAULT; - } - } - else /*CDROMREADCOOKED*/ - { if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1; - DTEN_LOW; - insb(DATA_PORT,buf,CD_FRAMESIZE); - if(copy_to_user((void *) arg, &buf, CD_FRAMESIZE)) - return -EFAULT; - } - } - break; - case CDROMSEEK: /*seek msf address*/ - if(copy_from_user(&msf, (void *) arg, sizeof msf)) +*/ +#if AZT_PRIVATE_IOCTLS + case CDROMREADCOOKED: /*read data in mode 1 (2048 Bytes) */ + case CDROMREADRAW: /*read data in mode 2 (2336 Bytes) */ + { + if (copy_from_user(&msf, (void *) arg, sizeof msf)) + return -EFAULT; + /* convert to bcd */ + azt_bin2bcd(&msf.cdmsf_min0); + azt_bin2bcd(&msf.cdmsf_sec0); + azt_bin2bcd(&msf.cdmsf_frame0); + msf.cdmsf_min1 = 0; + msf.cdmsf_sec1 = 0; + msf.cdmsf_frame1 = 1; /*read only one frame */ + azt_Play.start.min = msf.cdmsf_min0; + azt_Play.start.sec = msf.cdmsf_sec0; + azt_Play.start.frame = msf.cdmsf_frame0; + azt_Play.end.min = msf.cdmsf_min1; + azt_Play.end.sec = msf.cdmsf_sec1; + azt_Play.end.frame = msf.cdmsf_frame1; + if (cmd == CDROMREADRAW) { + if (DiskInfo.xa) { + return -1; /*XA Disks can't be read raw */ + } else { + if (sendAztCmd + (ACMD_PLAY_READ_RAW, + &azt_Play)) + return -1; + DTEN_LOW; + insb(DATA_PORT, buf, + CD_FRAMESIZE_RAW); + if (copy_to_user + ((void *) arg, &buf, + CD_FRAMESIZE_RAW)) + return -EFAULT; + } + } else + /*CDROMREADCOOKED*/ { + if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) + return -1; + DTEN_LOW; + insb(DATA_PORT, buf, CD_FRAMESIZE); + if (copy_to_user + ((void *) arg, &buf, CD_FRAMESIZE)) + return -EFAULT; + } + } + break; + case CDROMSEEK: /*seek msf address */ + if (copy_from_user(&msf, (void *) arg, sizeof msf)) return -EFAULT; /* convert to bcd */ azt_bin2bcd(&msf.cdmsf_min0); @@ -1397,18 +1526,20 @@ azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame); azt_Play.start.min = msf.cdmsf_min0; azt_Play.start.sec = msf.cdmsf_sec0; azt_Play.start.frame = msf.cdmsf_frame0; - if (aztSeek(&azt_Play)) return -1; - break; -#endif /*end of incompatible code*/ - case CDROMREADMODE1: /*set read data in mode 1*/ - return aztSetDiskType(AZT_MODE_1); - case CDROMREADMODE2: /*set read data in mode 2*/ - return aztSetDiskType(AZT_MODE_2); + if (aztSeek(&azt_Play)) + return -1; + break; +#endif /*end of incompatible code */ + case CDROMREADMODE1: /*set read data in mode 1 */ + return aztSetDiskType(AZT_MODE_1); + case CDROMREADMODE2: /*set read data in mode 2 */ + return aztSetDiskType(AZT_MODE_2); default: return -EINVAL; } #ifdef AZT_DEBUG - printk("aztcd: exiting aztcd_ioctl Command:%x Time:%li\n",cmd,jiffies); + printk("aztcd: exiting aztcd_ioctl Command:%x Time:%li\n", cmd, + jiffies); #endif return 0; } @@ -1418,131 +1549,139 @@ azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame); * When Linux gets variable block sizes this will probably go away. */ static void azt_transfer(void) -{ +{ #ifdef AZT_TEST - printk("aztcd: executing azt_transfer Time:%li\n",jiffies); + printk("aztcd: executing azt_transfer Time:%li\n", jiffies); #endif - if (CURRENT_VALID) { - while (CURRENT -> nr_sectors) { - int bn = CURRENT -> sector / 4; - int i; - for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i) - ; - if (i < AZT_BUF_SIZ) { - int offs = (i * 4 + (CURRENT -> sector & 3)) * 512; - int nr_sectors = 4 - (CURRENT -> sector & 3); - if (azt_buf_out != i) { - azt_buf_out = i; - if (azt_buf_bn[i] != bn) { - azt_buf_out = -1; - continue; - } + if (CURRENT_VALID) { + while (CURRENT->nr_sectors) { + int bn = CURRENT->sector / 4; + int i; + for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; + ++i); + if (i < AZT_BUF_SIZ) { + int offs = + (i * 4 + (CURRENT->sector & 3)) * 512; + int nr_sectors = 4 - (CURRENT->sector & 3); + if (azt_buf_out != i) { + azt_buf_out = i; + if (azt_buf_bn[i] != bn) { + azt_buf_out = -1; + continue; + } + } + if (nr_sectors > CURRENT->nr_sectors) + nr_sectors = CURRENT->nr_sectors; + memcpy(CURRENT->buffer, azt_buf + offs, + nr_sectors * 512); + CURRENT->nr_sectors -= nr_sectors; + CURRENT->sector += nr_sectors; + CURRENT->buffer += nr_sectors * 512; + } else { + azt_buf_out = -1; + break; + } + } } - if (nr_sectors > CURRENT -> nr_sectors) - nr_sectors = CURRENT -> nr_sectors; - memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512); - CURRENT -> nr_sectors -= nr_sectors; - CURRENT -> sector += nr_sectors; - CURRENT -> buffer += nr_sectors * 512; - } else { - azt_buf_out = -1; - break; - } - } - } } static void do_aztcd_request(request_queue_t * q) { #ifdef AZT_TEST - printk(" do_aztcd_request(%ld+%ld) Time:%li\n", CURRENT -> sector, CURRENT -> nr_sectors,jiffies); + printk(" do_aztcd_request(%ld+%ld) Time:%li\n", CURRENT->sector, + CURRENT->nr_sectors, jiffies); #endif - if (DiskInfo.audio) - { printk("aztcd: Error, tried to mount an Audio CD\n"); - end_request(0); - return; - } - azt_transfer_is_active = 1; - while (CURRENT_VALID) { - if (CURRENT->bh) { - if (!buffer_locked(CURRENT->bh)) - panic(DEVICE_NAME ": block not locked"); - } - azt_transfer(); - if (CURRENT -> nr_sectors == 0) { - end_request(1); - } else { - azt_buf_out = -1; /* Want to read a block not in buffer */ - if (azt_state == AZT_S_IDLE) { - if ((!aztTocUpToDate)||aztDiskChanged) { - if (aztUpdateToc() < 0) { - while (CURRENT_VALID) - end_request(0); - break; - } + if (DiskInfo.audio) { + printk("aztcd: Error, tried to mount an Audio CD\n"); + end_request(0); + return; + } + azt_transfer_is_active = 1; + while (CURRENT_VALID) { + if (CURRENT->bh) { + if (!buffer_locked(CURRENT->bh)) + panic(DEVICE_NAME ": block not locked"); + } + azt_transfer(); + if (CURRENT->nr_sectors == 0) { + end_request(1); + } else { + azt_buf_out = -1; /* Want to read a block not in buffer */ + if (azt_state == AZT_S_IDLE) { + if ((!aztTocUpToDate) || aztDiskChanged) { + if (aztUpdateToc() < 0) { + while (CURRENT_VALID) + end_request(0); + break; + } + } + azt_state = AZT_S_START; + AztTries = 5; + SET_TIMER(azt_poll, HZ / 100); + } + break; + } } - azt_state = AZT_S_START; - AztTries = 5; - SET_TIMER(azt_poll, HZ/100); - } - break; - } - } - azt_transfer_is_active = 0; + azt_transfer_is_active = 0; #ifdef AZT_TEST2 - printk("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", \ - azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]); - printk(" do_aztcd_request ends Time:%li\n",jiffies); + printk + ("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", + azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]); + printk(" do_aztcd_request ends Time:%li\n", jiffies); #endif } static void azt_invalidate_buffers(void) -{ int i; +{ + int i; #ifdef AZT_DEBUG - printk("aztcd: executing azt_invalidate_buffers\n"); + printk("aztcd: executing azt_invalidate_buffers\n"); #endif - for (i = 0; i < AZT_BUF_SIZ; ++i) - azt_buf_bn[i] = -1; - azt_buf_out = -1; + for (i = 0; i < AZT_BUF_SIZ; ++i) + azt_buf_bn[i] = -1; + azt_buf_out = -1; } /* * Open the device special file. Check that a disk is in. */ int aztcd_open(struct inode *ip, struct file *fp) -{ int st; +{ + int st; #ifdef AZT_DEBUG printk("aztcd: starting aztcd_open\n"); #endif if (aztPresent == 0) - return -ENXIO; /* no hardware */ - - MOD_INC_USE_COUNT; - - if (!azt_open_count && azt_state == AZT_S_IDLE) - { azt_invalidate_buffers(); - - st = getAztStatus(); /* check drive status */ - if (st == -1) goto err_out; /* drive doesn't respond */ - - if (st & AST_DOOR_OPEN) - { /* close door, then get the status again. */ - printk("aztcd: Door Open?\n"); - aztCloseDoor(); - st = getAztStatus(); - } - - if ((st & AST_NOT_READY) || (st & AST_DSK_CHG)) /*no disk in drive or changed*/ - { printk("aztcd: Disk Changed or No Disk in Drive?\n"); - aztTocUpToDate=0; - } - if (aztUpdateToc()) goto err_out; - - } + return -ENXIO; /* no hardware */ + + MOD_INC_USE_COUNT; + + if (!azt_open_count && azt_state == AZT_S_IDLE) { + azt_invalidate_buffers(); + + st = getAztStatus(); /* check drive status */ + if (st == -1) + goto err_out; /* drive doesn't respond */ + + if (st & AST_DOOR_OPEN) { /* close door, then get the status again. */ + printk("aztcd: Door Open?\n"); + aztCloseDoor(); + st = getAztStatus(); + } + + if ((st & AST_NOT_READY) || (st & AST_DSK_CHG)) { /*no disk in drive or changed */ + printk + ("aztcd: Disk Changed or No Disk in Drive?\n"); + aztTocUpToDate = 0; + } + if (aztUpdateToc()) + goto err_out; + + } ++azt_open_count; aztLockDoor(); @@ -1551,7 +1690,7 @@ int aztcd_open(struct inode *ip, struct file *fp) #endif return 0; -err_out: + err_out: MOD_DEC_USE_COUNT; return -EIO; } @@ -1560,21 +1699,22 @@ err_out: /* * On close, we flush all azt blocks from the buffer cache. */ -static int aztcd_release(struct inode * inode, struct file * file) -{ +static int aztcd_release(struct inode *inode, struct file *file) +{ #ifdef AZT_DEBUG - printk("aztcd: executing aztcd_release\n"); - printk("inode: %p, inode->i_rdev: %x file: %p\n",inode,inode->i_rdev,file); + printk("aztcd: executing aztcd_release\n"); + printk("inode: %p, inode->i_rdev: %x file: %p\n", inode, + inode->i_rdev, file); #endif - MOD_DEC_USE_COUNT; - if (!--azt_open_count) { - azt_invalidate_buffers(); - aztUnlockDoor(); - if (azt_auto_eject) - aztSendCmd(ACMD_EJECT); - CLEAR_TIMER; - } - return 0; + MOD_DEC_USE_COUNT; + if (!--azt_open_count) { + azt_invalidate_buffers(); + aztUnlockDoor(); + if (azt_auto_eject) + aztSendCmd(ACMD_EJECT); + CLEAR_TIMER; + } + return 0; } @@ -1584,220 +1724,244 @@ static int aztcd_release(struct inode * inode, struct file * file) */ int __init aztcd_init(void) -{ long int count, max_count; +{ + long int count, max_count; unsigned char result[50]; int st; int i = 0; - if (azt_port == 0) - { printk("aztcd: no Aztech CD-ROM Initialization"); - return -EIO; + if (azt_port == 0) { + printk("aztcd: no Aztech CD-ROM Initialization"); + return -EIO; } - printk("aztcd: AZTECH, ORCHID, OKANO, WEARNES, TXC, CyDROM CD-ROM Driver\n"); + printk + ("aztcd: AZTECH, ORCHID, OKANO, WEARNES, TXC, CyDROM CD-ROM Driver\n"); printk("aztcd: (C) 1994-98 W.Zimmermann\n"); - if (azt_port == -1) - { printk("aztcd: KernelVersion=%s DriverVersion=%s For IDE/ATAPI-drives use ide-cd.c\n",UTS_RELEASE,AZT_VERSION); + if (azt_port == -1) { + printk + ("aztcd: KernelVersion=%s DriverVersion=%s For IDE/ATAPI-drives use ide-cd.c\n", + UTS_RELEASE, AZT_VERSION); + } else + printk + ("aztcd: DriverVersion=%s BaseAddress=0x%x For IDE/ATAPI-drives use ide-cd.c\n", + AZT_VERSION, azt_port); + printk + ("aztcd: If you have problems, read /usr/src/linux/Documentation/cdrom/aztcd\n"); + + +#ifdef AZT_SW32 /*CDROM connected to Soundwave32 card */ + if ((0xFF00 & inw(AZT_SW32_ID_REG)) != 0x4500) { + printk + ("aztcd: no Soundwave32 card detected at base:%x init:%x config:%x id:%x\n", + AZT_SW32_BASE_ADDR, AZT_SW32_INIT, + AZT_SW32_CONFIG_REG, AZT_SW32_ID_REG); + return -EIO; + } else { + printk(KERN_INFO + "aztcd: Soundwave32 card detected at %x Version %x\n", + AZT_SW32_BASE_ADDR, inw(AZT_SW32_ID_REG)); + outw(AZT_SW32_INIT, AZT_SW32_CONFIG_REG); + for (count = 0; count < 10000; count++); /*delay a bit */ } - else - printk("aztcd: DriverVersion=%s BaseAddress=0x%x For IDE/ATAPI-drives use ide-cd.c\n",AZT_VERSION,azt_port); - printk("aztcd: If you have problems, read /usr/src/linux/Documentation/cdrom/aztcd\n"); - - -#ifdef AZT_SW32 /*CDROM connected to Soundwave32 card*/ - if ((0xFF00 & inw(AZT_SW32_ID_REG)) != 0x4500) - { printk("aztcd: no Soundwave32 card detected at base:%x init:%x config:%x id:%x\n", - AZT_SW32_BASE_ADDR,AZT_SW32_INIT,AZT_SW32_CONFIG_REG,AZT_SW32_ID_REG); - return -EIO; - } - else - { printk(KERN_INFO "aztcd: Soundwave32 card detected at %x Version %x\n", - AZT_SW32_BASE_ADDR, inw(AZT_SW32_ID_REG)); - outw(AZT_SW32_INIT,AZT_SW32_CONFIG_REG); - for (count=0;count<10000;count++); /*delay a bit*/ - } -#endif +#endif /* check for presence of drive */ - if (azt_port == -1) /* autoprobing */ - { for (i=0;(azt_port_auto[i]!=0)&&(i<16);i++) - { azt_port = azt_port_auto[i]; - printk("aztcd: Autoprobing BaseAddress=0x%x \n",azt_port); - st = check_region(azt_port, 4); /*proprietary interfaces need 4 bytes*/ - if (st) continue; - - outb(POLLED,MODE_PORT); - inb(CMD_PORT); - inb(CMD_PORT); - outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/ - - aztTimeOutCount=0; - do { aztIndatum=inb(STATUS_PORT); - aztTimeOutCount++; - if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; - } while (aztIndatum&AFL_STATUS); - if (inb(DATA_PORT)==AFL_OP_OK) - break; - } - if ((azt_port_auto[i]==0)||(i==16)) - { printk("aztcd: no AZTECH CD-ROM drive found\n"); - return -EIO; - } - } - else /* no autoprobing */ - { if ((azt_port==0x1f0)||(azt_port==0x170)) - st = check_region(azt_port, 8); /*IDE-interfaces need 8 bytes*/ - else - st = check_region(azt_port, 4); /*proprietary interfaces need 4 bytes*/ - if (st) - { printk("aztcd: conflict, I/O port (%X) already used\n",azt_port); - return -EIO; - } - - if ((azt_port==0x1f0)||(azt_port==0x170)) - SWITCH_IDE_SLAVE; /*switch IDE interface to slave configuration*/ - - outb(POLLED,MODE_PORT); - inb(CMD_PORT); - inb(CMD_PORT); - outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/ - - aztTimeOutCount=0; - do { aztIndatum=inb(STATUS_PORT); - aztTimeOutCount++; - if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; - } while (aztIndatum&AFL_STATUS); - - if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK? If not, reset and try again*/ - { + if (azt_port == -1) { /* autoprobing */ + for (i = 0; (azt_port_auto[i] != 0) && (i < 16); i++) { + azt_port = azt_port_auto[i]; + printk("aztcd: Autoprobing BaseAddress=0x%x \n", + azt_port); + st = check_region(azt_port, 4); /*proprietary interfaces need 4 bytes */ + if (st) + continue; + + outb(POLLED, MODE_PORT); + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_GET_VERSION, CMD_PORT); /*Try to get version info */ + + aztTimeOutCount = 0; + do { + aztIndatum = inb(STATUS_PORT); + aztTimeOutCount++; + if (aztTimeOutCount >= AZT_FAST_TIMEOUT) + break; + } while (aztIndatum & AFL_STATUS); + if (inb(DATA_PORT) == AFL_OP_OK) + break; + } + if ((azt_port_auto[i] == 0) || (i == 16)) { + printk("aztcd: no AZTECH CD-ROM drive found\n"); + return -EIO; + } + } else { /* no autoprobing */ + if ((azt_port == 0x1f0) || (azt_port == 0x170)) + st = check_region(azt_port, 8); /*IDE-interfaces need 8 bytes */ + else + st = check_region(azt_port, 4); /*proprietary interfaces need 4 bytes */ + if (st) { + printk + ("aztcd: conflict, I/O port (%X) already used\n", + azt_port); + return -EIO; + } + + if ((azt_port == 0x1f0) || (azt_port == 0x170)) + SWITCH_IDE_SLAVE; /*switch IDE interface to slave configuration */ + + outb(POLLED, MODE_PORT); + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_GET_VERSION, CMD_PORT); /*Try to get version info */ + + aztTimeOutCount = 0; + do { + aztIndatum = inb(STATUS_PORT); + aztTimeOutCount++; + if (aztTimeOutCount >= AZT_FAST_TIMEOUT) + break; + } while (aztIndatum & AFL_STATUS); + + if (inb(DATA_PORT) != AFL_OP_OK) { /*OP_OK? If not, reset and try again */ #ifndef MODULE - if (azt_cont!=0x79) - { printk("aztcd: no AZTECH CD-ROM drive found-Try boot parameter aztcd=<BaseAddress>,0x79\n"); - return -EIO; - } -#else - if (0) - { - } -#endif - else - { printk("aztcd: drive reset - please wait\n"); - for (count=0;count<50;count++) - { inb(STATUS_PORT); /*removing all data from earlier tries*/ - inb(DATA_PORT); - } - outb(POLLED,MODE_PORT); - inb(CMD_PORT); - inb(CMD_PORT); - getAztStatus(); /*trap errors*/ - outb(ACMD_SOFT_RESET,CMD_PORT); /*send reset*/ - STEN_LOW; - if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK?*/ - { printk("aztcd: no AZTECH CD-ROM drive found\n"); - return -EIO; - } - - for (count = 0; count < AZT_TIMEOUT; count++) - barrier(); /* Stop gcc 2.96 being smart */ - - if ((st=getAztStatus())==-1) - { printk("aztcd: Drive Status Error Status=%x\n",st); - return -EIO; - } + if (azt_cont != 0x79) { + printk + ("aztcd: no AZTECH CD-ROM drive found-Try boot parameter aztcd=<BaseAddress>,0x79\n"); + return -EIO; + } +#else + if (0) { + } +#endif + else { + printk + ("aztcd: drive reset - please wait\n"); + for (count = 0; count < 50; count++) { + inb(STATUS_PORT); /*removing all data from earlier tries */ + inb(DATA_PORT); + } + outb(POLLED, MODE_PORT); + inb(CMD_PORT); + inb(CMD_PORT); + getAztStatus(); /*trap errors */ + outb(ACMD_SOFT_RESET, CMD_PORT); /*send reset */ + STEN_LOW; + if (inb(DATA_PORT) != AFL_OP_OK) { /*OP_OK? */ + printk + ("aztcd: no AZTECH CD-ROM drive found\n"); + return -EIO; + } + + for (count = 0; count < AZT_TIMEOUT; + count++) + barrier(); /* Stop gcc 2.96 being smart */ + + if ((st = getAztStatus()) == -1) { + printk + ("aztcd: Drive Status Error Status=%x\n", + st); + return -EIO; + } #ifdef AZT_DEBUG - printk("aztcd: Status = %x\n",st); + printk("aztcd: Status = %x\n", st); #endif - outb(POLLED,MODE_PORT); - inb(CMD_PORT); - inb(CMD_PORT); - outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/ - STEN_LOW; - OP_OK; - } - } + outb(POLLED, MODE_PORT); + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_GET_VERSION, CMD_PORT); /*GetVersion */ + STEN_LOW; + OP_OK; + } + } } - - azt_init_end=1; + + azt_init_end = 1; STEN_LOW; - result[0]=inb(DATA_PORT); /*reading in a null byte???*/ - for (count=1;count<50;count++) /*Reading version string*/ - { aztTimeOutCount=0; /*here we must implement STEN_LOW differently*/ - do { aztIndatum=inb(STATUS_PORT);/*because we want to exit by timeout*/ - aztTimeOutCount++; - if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; - } while (aztIndatum&AFL_STATUS); - if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; /*all chars read?*/ - result[count]=inb(DATA_PORT); - } - if (count>30) max_count=30; /*print max.30 chars of the version string*/ - else max_count=count; + result[0] = inb(DATA_PORT); /*reading in a null byte??? */ + for (count = 1; count < 50; count++) { /*Reading version string */ + aztTimeOutCount = 0; /*here we must implement STEN_LOW differently */ + do { + aztIndatum = inb(STATUS_PORT); /*because we want to exit by timeout */ + aztTimeOutCount++; + if (aztTimeOutCount >= AZT_FAST_TIMEOUT) + break; + } while (aztIndatum & AFL_STATUS); + if (aztTimeOutCount >= AZT_FAST_TIMEOUT) + break; /*all chars read? */ + result[count] = inb(DATA_PORT); + } + if (count > 30) + max_count = 30; /*print max.30 chars of the version string */ + else + max_count = count; printk(KERN_INFO "aztcd: FirmwareVersion="); - for (count=1;count<max_count;count++) printk("%c",result[count]); + for (count = 1; count < max_count; count++) + printk("%c", result[count]); printk("<<>> "); - if ((result[1]=='A')&&(result[2]=='Z')&&(result[3]=='T')) - { printk("AZTECH drive detected\n"); /*AZTECH*/ - } - else if ((result[2]=='C')&&(result[3]=='D')&&(result[4]=='D')) - { printk("ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/ - } - else if ((result[1]==0x03)&&(result[2]=='5')) - { printk("TXC or CyCDROM drive detected\n"); /*Conrad TXC, CyCDROM*/ - } - else /*OTHERS or none*/ - { printk("\nunknown drive or firmware version detected\n"); - printk("aztcd may not run stable, if you want to try anyhow,\n"); - printk("boot with: aztcd=<BaseAddress>,0x79\n"); - if ((azt_cont!=0x79)) - { printk("aztcd: FirmwareVersion="); - for (count=1;count<5;count++) printk("%c",result[count]); - printk("<<>> "); - printk("Aborted\n"); - return -EIO; - } - } - devfs_register (NULL, "aztcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, &azt_fops, NULL); - if (devfs_register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) - { + if ((result[1] == 'A') && (result[2] == 'Z') && (result[3] == 'T')) { + printk("AZTECH drive detected\n"); + /*AZTECH*/} + else if ((result[2] == 'C') && (result[3] == 'D') + && (result[4] == 'D')) { + printk("ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES */ + } else if ((result[1] == 0x03) && (result[2] == '5')) { + printk("TXC or CyCDROM drive detected\n"); /*Conrad TXC, CyCDROM */ + } else { /*OTHERS or none */ + printk("\nunknown drive or firmware version detected\n"); + printk + ("aztcd may not run stable, if you want to try anyhow,\n"); + printk("boot with: aztcd=<BaseAddress>,0x79\n"); + if ((azt_cont != 0x79)) { + printk("aztcd: FirmwareVersion="); + for (count = 1; count < 5; count++) + printk("%c", result[count]); + printk("<<>> "); + printk("Aborted\n"); + return -EIO; + } + } + devfs_register(NULL, "aztcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &azt_fops, NULL); + if (devfs_register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) { printk("aztcd: Unable to get major %d for Aztech CD-ROM\n", MAJOR_NR); - return -EIO; + return -EIO; } blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); blksize_size[MAJOR_NR] = aztcd_blocksizes; read_ahead[MAJOR_NR] = 4; - register_disk(NULL, MKDEV(MAJOR_NR,0), 1, &azt_fops, 0); + register_disk(NULL, MKDEV(MAJOR_NR, 0), 1, &azt_fops, 0); + + if ((azt_port == 0x1f0) || (azt_port == 0x170)) + request_region(azt_port, 8, "aztcd"); /*IDE-interface */ + else + request_region(azt_port, 4, "aztcd"); /*proprietary interface */ - if ((azt_port==0x1f0)||(azt_port==0x170)) - request_region(azt_port, 8, "aztcd"); /*IDE-interface*/ - else - request_region(azt_port, 4, "aztcd"); /*proprietary interface*/ - azt_invalidate_buffers(); aztPresent = 1; aztCloseDoor(); - return (0); + return (0); } void __exit aztcd_exit(void) { - devfs_unregister(devfs_find_handle(NULL, "aztcd", 0, 0, DEVFS_SPECIAL_BLK, - 0)); - if ((devfs_unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) - { printk("What's that: can't unregister aztcd\n"); - return; - } - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - if ((azt_port==0x1f0)||(azt_port==0x170)) - { SWITCH_IDE_MASTER; - release_region(azt_port,8); /*IDE-interface*/ - } - else - release_region(azt_port,4); /*proprietary interface*/ - printk(KERN_INFO "aztcd module released.\n"); -} + devfs_unregister(devfs_find_handle + (NULL, "aztcd", 0, 0, DEVFS_SPECIAL_BLK, 0)); + if ((devfs_unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) { + printk("What's that: can't unregister aztcd\n"); + return; + } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + if ((azt_port == 0x1f0) || (azt_port == 0x170)) { + SWITCH_IDE_MASTER; + release_region(azt_port, 8); /*IDE-interface */ + } else + release_region(azt_port, 4); /*proprietary interface */ + printk(KERN_INFO "aztcd module released.\n"); +} #ifdef MODULE module_init(aztcd_init); @@ -1810,405 +1974,485 @@ module_exit(aztcd_exit); */ static void azt_poll(void) { - int st = 0; - int loop_ctl = 1; - int skip = 0; - - if (azt_error) { - if (aztSendCmd(ACMD_GET_ERROR)) RETURN("azt_poll 1"); - STEN_LOW; - azt_error=inb(DATA_PORT)&0xFF; - printk("aztcd: I/O error 0x%02x\n", azt_error); - azt_invalidate_buffers(); + int st = 0; + int loop_ctl = 1; + int skip = 0; + + if (azt_error) { + if (aztSendCmd(ACMD_GET_ERROR)) + RETURN("azt_poll 1"); + STEN_LOW; + azt_error = inb(DATA_PORT) & 0xFF; + printk("aztcd: I/O error 0x%02x\n", azt_error); + azt_invalidate_buffers(); #ifdef WARN_IF_READ_FAILURE - if (AztTries == 5) - printk("aztcd: Read of Block %d Failed - Maybe Audio Disk?\n", azt_next_bn); + if (AztTries == 5) + printk + ("aztcd: Read of Block %d Failed - Maybe Audio Disk?\n", + azt_next_bn); #endif - if (!AztTries--) { - printk("aztcd: Read of Block %d Failed, Maybe Audio Disk? Giving up\n", azt_next_bn); - if (azt_transfer_is_active) { - AztTries = 0; - loop_ctl = 0; - } - if (CURRENT_VALID) - end_request(0); - AztTries = 5; + if (!AztTries--) { + printk + ("aztcd: Read of Block %d Failed, Maybe Audio Disk? Giving up\n", + azt_next_bn); + if (azt_transfer_is_active) { + AztTries = 0; + loop_ctl = 0; + } + if (CURRENT_VALID) + end_request(0); + AztTries = 5; + } + azt_error = 0; + azt_state = AZT_S_STOP; } - azt_error = 0; - azt_state = AZT_S_STOP; - } - while (loop_ctl) - { - loop_ctl = 0; /* each case must flip this back to 1 if we want - to come back up here */ - switch (azt_state) { + while (loop_ctl) { + loop_ctl = 0; /* each case must flip this back to 1 if we want + to come back up here */ + switch (azt_state) { - case AZT_S_IDLE: + case AZT_S_IDLE: #ifdef AZT_TEST3 - if (azt_state!=azt_state_old) { - azt_state_old=azt_state; - printk("AZT_S_IDLE\n"); - } + if (azt_state != azt_state_old) { + azt_state_old = azt_state; + printk("AZT_S_IDLE\n"); + } #endif - return; + return; - case AZT_S_START: + case AZT_S_START: #ifdef AZT_TEST3 - if (azt_state!=azt_state_old) { - azt_state_old=azt_state; - printk("AZT_S_START\n"); - } + if (azt_state != azt_state_old) { + azt_state_old = azt_state; + printk("AZT_S_START\n"); + } #endif - if(aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 2"); /*result will be checked by aztStatus() */ - azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE; - AztTimeout = 3000; - break; - - case AZT_S_MODE: + if (aztSendCmd(ACMD_GET_STATUS)) + RETURN("azt_poll 2"); /*result will be checked by aztStatus() */ + azt_state = + azt_mode == 1 ? AZT_S_READ : AZT_S_MODE; + AztTimeout = 3000; + break; + + case AZT_S_MODE: #ifdef AZT_TEST3 - if (azt_state!=azt_state_old) { - azt_state_old=azt_state; - printk("AZT_S_MODE\n"); - } + if (azt_state != azt_state_old) { + azt_state_old = azt_state; + printk("AZT_S_MODE\n"); + } #endif - if (!skip) { - if ((st = aztStatus()) != -1) { - if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) { - aztDiskChanged = 1; - aztTocUpToDate = 0; - azt_invalidate_buffers(); - end_request(0); - printk("aztcd: Disk Changed or Not Ready 1 - Unmount Disk!\n"); - } - } else break; - } - skip = 0; - - if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) { - aztDiskChanged = 1; - aztTocUpToDate = 0; - printk("aztcd: Disk Changed or Not Ready 2 - Unmount Disk!\n"); - end_request(0); - printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n"); - if (azt_transfer_is_active) { - azt_state = AZT_S_START; - loop_ctl = 1; /* goto immediately */ - break; - } - azt_state = AZT_S_IDLE; - while (CURRENT_VALID) - end_request(0); - return; - } - + if (!skip) { + if ((st = aztStatus()) != -1) { + if ((st & AST_DSK_CHG) + || (st & AST_NOT_READY)) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + azt_invalidate_buffers(); + end_request(0); + printk + ("aztcd: Disk Changed or Not Ready 1 - Unmount Disk!\n"); + } + } else + break; + } + skip = 0; + + if ((st & AST_DOOR_OPEN) || (st & AST_NOT_READY)) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + printk + ("aztcd: Disk Changed or Not Ready 2 - Unmount Disk!\n"); + end_request(0); + printk((st & AST_DOOR_OPEN) ? + "aztcd: door open\n" : + "aztcd: disk removed\n"); + if (azt_transfer_is_active) { + azt_state = AZT_S_START; + loop_ctl = 1; /* goto immediately */ + break; + } + azt_state = AZT_S_IDLE; + while (CURRENT_VALID) + end_request(0); + return; + } + /* if (aztSendCmd(ACMD_SET_MODE)) RETURN("azt_poll 3"); outb(0x01, DATA_PORT); PA_OK; STEN_LOW; -*/ if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 4"); - STEN_LOW; - azt_mode = 1; - azt_state = AZT_S_READ; - AztTimeout = 3000; +*/ + if (aztSendCmd(ACMD_GET_STATUS)) + RETURN("azt_poll 4"); + STEN_LOW; + azt_mode = 1; + azt_state = AZT_S_READ; + AztTimeout = 3000; - break; + break; - case AZT_S_READ: + case AZT_S_READ: #ifdef AZT_TEST3 - if (azt_state!=azt_state_old) { - azt_state_old=azt_state; - printk("AZT_S_READ\n"); - } + if (azt_state != azt_state_old) { + azt_state_old = azt_state; + printk("AZT_S_READ\n"); + } #endif - if (!skip) { - if ((st = aztStatus()) != -1) { - if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) { - aztDiskChanged = 1; - aztTocUpToDate = 0; - azt_invalidate_buffers(); - printk("aztcd: Disk Changed or Not Ready 3 - Unmount Disk!\n"); - end_request(0); - } - } else break; - } - - skip = 0; - if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) { - aztDiskChanged = 1; - aztTocUpToDate = 0; - printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n"); - if (azt_transfer_is_active) { - azt_state = AZT_S_START; - loop_ctl = 1; - break; - } - azt_state = AZT_S_IDLE; - while (CURRENT_VALID) - end_request(0); - return; - } - - if (CURRENT_VALID) { - struct azt_Play_msf msf; - int i; - azt_next_bn = CURRENT -> sector / 4; - azt_hsg2msf(azt_next_bn, &msf.start); - i = 0; - /* find out in which track we are */ - while (azt_msf2hsg(&msf.start)>azt_msf2hsg(&Toc[++i].trackTime)) {}; - if (azt_msf2hsg(&msf.start)<azt_msf2hsg(&Toc[i].trackTime)-AZT_BUF_SIZ) - { azt_read_count=AZT_BUF_SIZ; /*fast, because we read ahead*/ - /*azt_read_count=CURRENT->nr_sectors; slow, no read ahead*/ - } - else /* don't read beyond end of track */ -#if AZT_MULTISESSION - { azt_read_count=(azt_msf2hsg(&Toc[i].trackTime)/4)*4-azt_msf2hsg(&msf.start); - if (azt_read_count < 0) azt_read_count=0; - if (azt_read_count > AZT_BUF_SIZ) azt_read_count=AZT_BUF_SIZ; - printk("aztcd: warning - trying to read beyond end of track\n"); + if (!skip) { + if ((st = aztStatus()) != -1) { + if ((st & AST_DSK_CHG) + || (st & AST_NOT_READY)) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + azt_invalidate_buffers(); + printk + ("aztcd: Disk Changed or Not Ready 3 - Unmount Disk!\n"); + end_request(0); + } + } else + break; + } + + skip = 0; + if ((st & AST_DOOR_OPEN) || (st & AST_NOT_READY)) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + printk((st & AST_DOOR_OPEN) ? + "aztcd: door open\n" : + "aztcd: disk removed\n"); + if (azt_transfer_is_active) { + azt_state = AZT_S_START; + loop_ctl = 1; + break; + } + azt_state = AZT_S_IDLE; + while (CURRENT_VALID) + end_request(0); + return; + } + + if (CURRENT_VALID) { + struct azt_Play_msf msf; + int i; + azt_next_bn = CURRENT->sector / 4; + azt_hsg2msf(azt_next_bn, &msf.start); + i = 0; + /* find out in which track we are */ + while (azt_msf2hsg(&msf.start) > + azt_msf2hsg(&Toc[++i].trackTime)) { + }; + if (azt_msf2hsg(&msf.start) < + azt_msf2hsg(&Toc[i].trackTime) - + AZT_BUF_SIZ) { + azt_read_count = AZT_BUF_SIZ; /*fast, because we read ahead */ + /*azt_read_count=CURRENT->nr_sectors; slow, no read ahead */ + } else /* don't read beyond end of track */ +#if AZT_MULTISESSION + { + azt_read_count = + (azt_msf2hsg(&Toc[i].trackTime) + / 4) * 4 - + azt_msf2hsg(&msf.start); + if (azt_read_count < 0) + azt_read_count = 0; + if (azt_read_count > AZT_BUF_SIZ) + azt_read_count = + AZT_BUF_SIZ; + printk + ("aztcd: warning - trying to read beyond end of track\n"); /* printk("%i %i %li %li\n",i,azt_read_count,azt_msf2hsg(&msf.start),azt_msf2hsg(&Toc[i].trackTime)); -*/ } +*/ } #else - { azt_read_count=AZT_BUF_SIZ; - } + { + azt_read_count = AZT_BUF_SIZ; + } #endif - msf.end.min = 0; - msf.end.sec = 0; - msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/ + msf.end.min = 0; + msf.end.sec = 0; + msf.end.frame = azt_read_count; /*Mitsumi here reads 0xffffff sectors */ #ifdef AZT_TEST3 - printk("---reading msf-address %x:%x:%x %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame); - printk("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", \ - azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]); -#endif - if (azt_read_mode==AZT_MODE_2) - { sendAztCmd(ACMD_PLAY_READ_RAW, &msf); /*XA disks in raw mode*/ - } - else - { sendAztCmd(ACMD_PLAY_READ, &msf); /*others in cooked mode*/ - } - azt_state = AZT_S_DATA; - AztTimeout = READ_TIMEOUT; - } else { - azt_state = AZT_S_STOP; - loop_ctl = 1; - break; - } - - break; - - - case AZT_S_DATA: + printk + ("---reading msf-address %x:%x:%x %x:%x:%x\n", + msf.start.min, msf.start.sec, + msf.start.frame, msf.end.min, + msf.end.sec, msf.end.frame); + printk + ("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", + azt_next_bn, azt_buf_in, azt_buf_out, + azt_buf_bn[azt_buf_in]); +#endif + if (azt_read_mode == AZT_MODE_2) { + sendAztCmd(ACMD_PLAY_READ_RAW, &msf); /*XA disks in raw mode */ + } else { + sendAztCmd(ACMD_PLAY_READ, &msf); /*others in cooked mode */ + } + azt_state = AZT_S_DATA; + AztTimeout = READ_TIMEOUT; + } else { + azt_state = AZT_S_STOP; + loop_ctl = 1; + break; + } + + break; + + + case AZT_S_DATA: #ifdef AZT_TEST3 - if (azt_state!=azt_state_old) { - azt_state_old=azt_state; - printk("AZT_S_DATA\n"); - } + if (azt_state != azt_state_old) { + azt_state_old = azt_state; + printk("AZT_S_DATA\n"); + } #endif - st = inb(STATUS_PORT) & AFL_STATUSorDATA; + st = inb(STATUS_PORT) & AFL_STATUSorDATA; - switch (st) { + switch (st) { - case AFL_DATA: + case AFL_DATA: #ifdef AZT_TEST3 - if (st!=azt_st_old) { - azt_st_old=st; - printk("---AFL_DATA st:%x\n",st); - } + if (st != azt_st_old) { + azt_st_old = st; + printk("---AFL_DATA st:%x\n", st); + } #endif - if (!AztTries--) { - printk("aztcd: Read of Block %d Failed, Maybe Audio Disk ? Giving up\n", azt_next_bn); - if (azt_transfer_is_active) { - AztTries = 0; - break; - } - if (CURRENT_VALID) - end_request(0); - AztTries = 5; - } - azt_state = AZT_S_START; - AztTimeout = READ_TIMEOUT; - loop_ctl = 1; - break; - - case AFL_STATUSorDATA: + if (!AztTries--) { + printk + ("aztcd: Read of Block %d Failed, Maybe Audio Disk ? Giving up\n", + azt_next_bn); + if (azt_transfer_is_active) { + AztTries = 0; + break; + } + if (CURRENT_VALID) + end_request(0); + AztTries = 5; + } + azt_state = AZT_S_START; + AztTimeout = READ_TIMEOUT; + loop_ctl = 1; + break; + + case AFL_STATUSorDATA: #ifdef AZT_TEST3 - if (st!=azt_st_old) { - azt_st_old=st; - printk("---AFL_STATUSorDATA st:%x\n",st); - } + if (st != azt_st_old) { + azt_st_old = st; + printk + ("---AFL_STATUSorDATA st:%x\n", + st); + } #endif - break; + break; - default: + default: #ifdef AZT_TEST3 - if (st!=azt_st_old) { - azt_st_old=st; - printk("---default: st:%x\n",st); - } + if (st != azt_st_old) { + azt_st_old = st; + printk("---default: st:%x\n", st); + } #endif - AztTries = 5; - if (!CURRENT_VALID && azt_buf_in == azt_buf_out) { - azt_state = AZT_S_STOP; - loop_ctl = 1; - break; - } - if (azt_read_count<=0) - printk("aztcd: warning - try to read 0 frames\n"); - while (azt_read_count) /*??? fast read ahead loop*/ - { azt_buf_bn[azt_buf_in] = -1; - DTEN_LOW; /*??? unsolved problem, very - seldom we get timeouts - here, don't now the real - reason. With my drive this - sometimes also happens with - Aztech's original driver under - DOS. Is it a hardware bug? - I tried to recover from such - situations here. Zimmermann*/ - if (aztTimeOutCount>=AZT_TIMEOUT) - { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in); - printk("azt_transfer_is_active:%x\n",azt_transfer_is_active); - azt_read_count=0; - azt_state = AZT_S_STOP; - loop_ctl = 1; - end_request(1); /*should we have here (1) or (0)? */ - } - else - { if (azt_read_mode==AZT_MODE_2) - { insb(DATA_PORT, azt_buf + CD_FRAMESIZE_RAW * azt_buf_in, CD_FRAMESIZE_RAW); - } - else - { insb(DATA_PORT, azt_buf + CD_FRAMESIZE * azt_buf_in, CD_FRAMESIZE); - } - azt_read_count--; + AztTries = 5; + if (!CURRENT_VALID + && azt_buf_in == azt_buf_out) { + azt_state = AZT_S_STOP; + loop_ctl = 1; + break; + } + if (azt_read_count <= 0) + printk + ("aztcd: warning - try to read 0 frames\n"); + while (azt_read_count) { /*??? fast read ahead loop */ + azt_buf_bn[azt_buf_in] = -1; + DTEN_LOW; /*??? unsolved problem, very + seldom we get timeouts + here, don't now the real + reason. With my drive this + sometimes also happens with + Aztech's original driver under + DOS. Is it a hardware bug? + I tried to recover from such + situations here. Zimmermann */ + if (aztTimeOutCount >= AZT_TIMEOUT) { + printk + ("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", + azt_read_count, + CURRENT->nr_sectors, + azt_buf_in); + printk + ("azt_transfer_is_active:%x\n", + azt_transfer_is_active); + azt_read_count = 0; + azt_state = AZT_S_STOP; + loop_ctl = 1; + end_request(1); /*should we have here (1) or (0)? */ + } else { + if (azt_read_mode == + AZT_MODE_2) { + insb(DATA_PORT, + azt_buf + + CD_FRAMESIZE_RAW + * azt_buf_in, + CD_FRAMESIZE_RAW); + } else { + insb(DATA_PORT, + azt_buf + + CD_FRAMESIZE * + azt_buf_in, + CD_FRAMESIZE); + } + azt_read_count--; #ifdef AZT_TEST3 - printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count); - printk("azt_next_bn:%d azt_buf_in:%d azt_buf_out:%d azt_buf_bn:%d\n", \ - azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]); + printk + ("AZT_S_DATA; ---I've read data- read_count: %d\n", + azt_read_count); + printk + ("azt_next_bn:%d azt_buf_in:%d azt_buf_out:%d azt_buf_bn:%d\n", + azt_next_bn, + azt_buf_in, + azt_buf_out, + azt_buf_bn + [azt_buf_in]); #endif - azt_buf_bn[azt_buf_in] = azt_next_bn++; - if (azt_buf_out == -1) - azt_buf_out = azt_buf_in; - azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1; - } - } - if (!azt_transfer_is_active) { - while (CURRENT_VALID) { - azt_transfer(); - if (CURRENT -> nr_sectors == 0) - end_request(1); - else - break; - } - } - - if (CURRENT_VALID - && (CURRENT -> sector / 4 < azt_next_bn || - CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) { - azt_state = AZT_S_STOP; - loop_ctl = 1; - break; - } - AztTimeout = READ_TIMEOUT; - if (azt_read_count==0) { - azt_state = AZT_S_STOP; - loop_ctl = 1; - break; - } - break; - } - break; - - - case AZT_S_STOP: + azt_buf_bn[azt_buf_in] = + azt_next_bn++; + if (azt_buf_out == -1) + azt_buf_out = + azt_buf_in; + azt_buf_in = + azt_buf_in + 1 == + AZT_BUF_SIZ ? 0 : + azt_buf_in + 1; + } + } + if (!azt_transfer_is_active) { + while (CURRENT_VALID) { + azt_transfer(); + if (CURRENT->nr_sectors == + 0) + end_request(1); + else + break; + } + } + + if (CURRENT_VALID + && (CURRENT->sector / 4 < azt_next_bn + || CURRENT->sector / 4 > + azt_next_bn + AZT_BUF_SIZ)) { + azt_state = AZT_S_STOP; + loop_ctl = 1; + break; + } + AztTimeout = READ_TIMEOUT; + if (azt_read_count == 0) { + azt_state = AZT_S_STOP; + loop_ctl = 1; + break; + } + break; + } + break; + + + case AZT_S_STOP: #ifdef AZT_TEST3 - if (azt_state!=azt_state_old) { - azt_state_old=azt_state; - printk("AZT_S_STOP\n"); - } + if (azt_state != azt_state_old) { + azt_state_old = azt_state; + printk("AZT_S_STOP\n"); + } #endif - if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count); - while (azt_read_count!=0) { - int i; - if ( !(inb(STATUS_PORT) & AFL_DATA) ) { - if (azt_read_mode==AZT_MODE_2) - for (i=0; i<CD_FRAMESIZE_RAW; i++) inb(DATA_PORT); - else - for (i=0; i<CD_FRAMESIZE; i++) inb(DATA_PORT); - } - azt_read_count--; - } - if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 5"); - azt_state = AZT_S_STOPPING; - AztTimeout = 1000; - break; - - case AZT_S_STOPPING: + if (azt_read_count != 0) + printk("aztcd: discard data=%x frames\n", + azt_read_count); + while (azt_read_count != 0) { + int i; + if (!(inb(STATUS_PORT) & AFL_DATA)) { + if (azt_read_mode == AZT_MODE_2) + for (i = 0; + i < CD_FRAMESIZE_RAW; + i++) + inb(DATA_PORT); + else + for (i = 0; + i < CD_FRAMESIZE; i++) + inb(DATA_PORT); + } + azt_read_count--; + } + if (aztSendCmd(ACMD_GET_STATUS)) + RETURN("azt_poll 5"); + azt_state = AZT_S_STOPPING; + AztTimeout = 1000; + break; + + case AZT_S_STOPPING: #ifdef AZT_TEST3 - if (azt_state!=azt_state_old) { - azt_state_old=azt_state; - printk("AZT_S_STOPPING\n"); - } + if (azt_state != azt_state_old) { + azt_state_old = azt_state; + printk("AZT_S_STOPPING\n"); + } #endif - if ((st = aztStatus()) == -1 && AztTimeout) - break; - - if ((st != -1) && ((st & AST_DSK_CHG)||(st & AST_NOT_READY))) { - aztDiskChanged = 1; - aztTocUpToDate = 0; - azt_invalidate_buffers(); - printk("aztcd: Disk Changed or Not Ready 4 - Unmount Disk!\n"); - end_request(0); - } + if ((st = aztStatus()) == -1 && AztTimeout) + break; + if ((st != -1) + && ((st & AST_DSK_CHG) + || (st & AST_NOT_READY))) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + azt_invalidate_buffers(); + printk + ("aztcd: Disk Changed or Not Ready 4 - Unmount Disk!\n"); + end_request(0); + } #ifdef AZT_TEST3 - printk("CURRENT_VALID %d azt_mode %d\n", - CURRENT_VALID, azt_mode); + printk("CURRENT_VALID %d azt_mode %d\n", + CURRENT_VALID, azt_mode); #endif - if (CURRENT_VALID) { - if (st != -1) { - if (azt_mode == 1) { - azt_state = AZT_S_READ; - loop_ctl = 1; - skip = 1; - break; - } else { - azt_state = AZT_S_MODE; - loop_ctl = 1; - skip = 1; - break; - } - } else { - azt_state = AZT_S_START; - AztTimeout = 1; - } - } else { - azt_state = AZT_S_IDLE; - return; - } - break; + if (CURRENT_VALID) { + if (st != -1) { + if (azt_mode == 1) { + azt_state = AZT_S_READ; + loop_ctl = 1; + skip = 1; + break; + } else { + azt_state = AZT_S_MODE; + loop_ctl = 1; + skip = 1; + break; + } + } else { + azt_state = AZT_S_START; + AztTimeout = 1; + } + } else { + azt_state = AZT_S_IDLE; + return; + } + break; + + default: + printk("aztcd: invalid state %d\n", azt_state); + return; + } /* case */ + } /* while */ + + + if (!AztTimeout--) { + printk("aztcd: timeout in state %d\n", azt_state); + azt_state = AZT_S_STOP; + if (aztSendCmd(ACMD_STOP)) + RETURN("azt_poll 6"); + STEN_LOW_WAIT; + }; - default: - printk("aztcd: invalid state %d\n", azt_state); - return; - } /* case */ - } /* while */ - - - if (!AztTimeout--) - { printk("aztcd: timeout in state %d\n", azt_state); - azt_state = AZT_S_STOP; - if (aztSendCmd(ACMD_STOP)) RETURN("azt_poll 6"); - STEN_LOW_WAIT; - }; - - SET_TIMER(azt_poll, HZ/100); + SET_TIMER(azt_poll, HZ / 100); } @@ -2217,28 +2461,34 @@ static void azt_poll(void) ########################################################################### */ static void azt_hsg2msf(long hsg, struct msf *msf) -{ hsg += 150; - msf -> min = hsg / 4500; +{ + hsg += 150; + msf->min = hsg / 4500; hsg %= 4500; - msf -> sec = hsg / 75; - msf -> frame = hsg % 75; + msf->sec = hsg / 75; + msf->frame = hsg % 75; #ifdef AZT_DEBUG - if (msf->min >=70) printk("aztcd: Error hsg2msf address Minutes\n"); - if (msf->sec >=60) printk("aztcd: Error hsg2msf address Seconds\n"); - if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n"); + if (msf->min >= 70) + printk("aztcd: Error hsg2msf address Minutes\n"); + if (msf->sec >= 60) + printk("aztcd: Error hsg2msf address Seconds\n"); + if (msf->frame >= 75) + printk("aztcd: Error hsg2msf address Frames\n"); #endif - azt_bin2bcd(&msf -> min); /* convert to BCD */ - azt_bin2bcd(&msf -> sec); - azt_bin2bcd(&msf -> frame); + azt_bin2bcd(&msf->min); /* convert to BCD */ + azt_bin2bcd(&msf->sec); + azt_bin2bcd(&msf->frame); } static long azt_msf2hsg(struct msf *mp) -{ return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75 - + azt_bcd2bin(mp -> min) * 4500 - CD_MSF_OFFSET; +{ + return azt_bcd2bin(mp->frame) + azt_bcd2bin(mp->sec) * 75 + + azt_bcd2bin(mp->min) * 4500 - CD_MSF_OFFSET; } static void azt_bin2bcd(unsigned char *p) -{ int u, t; +{ + int u, t; u = *p % 10; t = *p / 10; @@ -2246,7 +2496,8 @@ static void azt_bin2bcd(unsigned char *p) } static int azt_bcd2bin(unsigned char bcd) -{ return (bcd >> 4) * 10 + (bcd & 0xF); +{ + return (bcd >> 4) * 10 + (bcd & 0xF); } MODULE_LICENSE("GPL"); diff --git a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c index 85cc35b449e4..481297baf358 100644 --- a/drivers/cdrom/mcd.c +++ b/drivers/cdrom/mcd.c @@ -2,6 +2,7 @@ linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver Copyright (C) 1992 Martin Harriss + Portions Copyright (C) 2001 Red Hat martin@bdsi.com (no longer valid - where are you now, Martin?) @@ -74,7 +75,8 @@ module_init & module_exit. Torben Mathiasen <tmm@image.dk> - + September 2001 - Reformatted and cleaned up the code + Alan Cox <alan@redhat.com> */ #include <linux/module.h> @@ -117,22 +119,8 @@ static int mcd1xhold; /* Is the drive connected properly and responding?? */ static int mcdPresent; -#if 0 -#define TEST1 /* <int-..> */ -#define TEST2 /* do_mcd_req */ -#define TEST3 */ /* MCD_S_state */ -#define TEST4 /* QUICK_LOOP-counter */ -#define TEST5 */ /* port(1) state */ -#endif - -#if 1 #define QUICK_LOOP_DELAY udelay(45) /* use udelay */ #define QUICK_LOOP_COUNT 20 -#else -#define QUICK_LOOP_DELAY -#define QUICK_LOOP_COUNT 140 /* better wait constant time */ -#endif -/* #define DOUBLE_QUICK_ONLY */ #define CURRENT_VALID \ (!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \ @@ -158,12 +146,10 @@ enum mcd_state_e { static volatile enum mcd_state_e mcd_state = MCD_S_IDLE; static int mcd_mode = -1; static int MCMD_DATA_READ = MCMD_PLAY_READ; + #define READ_TIMEOUT 3000 -#define WORK_AROUND_MITSUMI_BUG_92 -#define WORK_AROUND_MITSUMI_BUG_93 -#ifdef WORK_AROUND_MITSUMI_BUG_93 + int mitsumi_bug_93_wait; -#endif /* WORK_AROUND_MITSUMI_BUG_93 */ static short mcd_port = CONFIG_MCD_BASE; /* used as "mcd" by "insmod" */ static int mcd_irq = CONFIG_MCD_IRQ; /* must directly follow mcd_port */ @@ -234,10 +220,8 @@ static int __init mcd_setup(char *str) mcd_port = ints[1]; if (ints[0] > 1) mcd_irq = ints[2]; -#ifdef WORK_AROUND_MITSUMI_BUG_93 if (ints[0] > 2) mitsumi_bug_93_wait = ints[3]; -#endif /* WORK_AROUND_MITSUMI_BUG_93 */ return 1; } @@ -248,23 +232,7 @@ __setup("mcd=", mcd_setup); static int mcd_media_changed(struct cdrom_device_info *cdi, int disc_nr) { - int retval; - - -#if 1 /* the below is not reliable */ return 0; -#endif - - if (cdi->dev) { - printk - ("mcd: Mitsumi CD-ROM request error: invalid device.\n"); - return 0; - } - - retval = mcdDiskChanged; - mcdDiskChanged = 0; - - return retval; } @@ -278,7 +246,8 @@ static int statusCmd(void) int st = -1, retry; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { - outb(MCMD_GET_STATUS, MCDPORT(0)); /* send get-status cmd */ + /* send get-status cmd */ + outb(MCMD_GET_STATUS, MCDPORT(0)); st = getMcdStatus(MCD_STATUS_DELAY); if (st != -1) @@ -335,8 +304,7 @@ static int mcd_tray_move(struct cdrom_device_info *cdi, int position) long msf2hsg(struct msf *mp) { - return bcd2bin(mp->frame) - + bcd2bin(mp->sec) * 75 + bcd2bin(mp->min) * 4500 - 150; + return bcd2bin(mp->frame) + bcd2bin(mp->sec) * 75 + bcd2bin(mp->min) * 4500 - 150; } @@ -538,18 +506,12 @@ int mcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, subchnl->cdsc_ctrl = qInfo.ctrl_addr >> 4; subchnl->cdsc_trk = bcd2bin(qInfo.track); subchnl->cdsc_ind = bcd2bin(qInfo.pointIndex); - subchnl->cdsc_absaddr.msf.minute = - bcd2bin(qInfo.diskTime.min); - subchnl->cdsc_absaddr.msf.second = - bcd2bin(qInfo.diskTime.sec); - subchnl->cdsc_absaddr.msf.frame = - bcd2bin(qInfo.diskTime.frame); - subchnl->cdsc_reladdr.msf.minute = - bcd2bin(qInfo.trackTime.min); - subchnl->cdsc_reladdr.msf.second = - bcd2bin(qInfo.trackTime.sec); - subchnl->cdsc_reladdr.msf.frame = - bcd2bin(qInfo.trackTime.frame); + subchnl->cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min); + subchnl->cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec); + subchnl->cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame); + subchnl->cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min); + subchnl->cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec); + subchnl->cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame); return (0); case CDROMVOLCTRL: /* Volume control */ @@ -594,8 +556,7 @@ static void mcd_transfer(void) for (i = 0; i < MCD_BUF_SIZ && mcd_buf_bn[i] != bn; ++i); if (i < MCD_BUF_SIZ) { - int offs = - (i * 4 + (CURRENT->sector & 3)) * 512; + int offs =(i * 4 + (CURRENT->sector & 3)) * 512; int nr_sectors = 4 - (CURRENT->sector & 3); if (mcd_buf_out != i) { mcd_buf_out = i; @@ -630,14 +591,10 @@ static void mcd_interrupt(int irq, void *dev_id, struct pt_regs *regs) int st; st = inb(MCDPORT(1)) & 0xFF; -#ifdef TEST1 - printk("<int1-%02X>", st); -#endif + test1(printk("<int1-%02X>", st)); if (!(st & MFL_STATUS)) { st = inb(MCDPORT(0)) & 0xFF; -#ifdef TEST1 - printk("<int0-%02X>", st); -#endif + test1(printk("<int0-%02X>", st)); if ((st & 0xFF) != 0xFF) mcd_error = st ? st & 0xFF : -1; } @@ -646,11 +603,10 @@ static void mcd_interrupt(int irq, void *dev_id, struct pt_regs *regs) static void do_mcd_request(request_queue_t * q) { -#ifdef TEST2 - printk(" do_mcd_request(%ld+%ld)\n", CURRENT->sector, - CURRENT->nr_sectors); -#endif - mcd_transfer_is_active = 1; + test2(printk(" do_mcd_request(%ld+%ld)\n", CURRENT->sector, + CURRENT->nr_sectors)); + + mcd_transfer_is_active = 1; while (CURRENT_VALID) { if (CURRENT->bh) { if (!buffer_locked(CURRENT->bh)) @@ -671,15 +627,14 @@ static void do_mcd_request(request_queue_t * q) } mcd_state = MCD_S_START; McdTries = 5; - SET_TIMER(mcd_poll, 1); + mcd_timer.function = mcd_poll; + mod_timer(&mcd_timer, jiffies + 1); } break; } } mcd_transfer_is_active = 0; -#ifdef TEST2 - printk(" do_mcd_request ends\n"); -#endif + test2(printk(" do_mcd_request ends\n")); } @@ -691,7 +646,7 @@ static void mcd_poll(unsigned long dummy) if (mcd_error) { if (mcd_error & 0xA5) { - printk("mcd: I/O error 0x%02x", mcd_error); + printk(KERN_ERR "mcd: I/O error 0x%02x", mcd_error); if (mcd_error & 0x80) printk(" (Door open)"); if (mcd_error & 0x20) @@ -718,14 +673,13 @@ static void mcd_poll(unsigned long dummy) mcd_invalidate_buffers(); #ifdef WARN_IF_READ_FAILURE if (McdTries == MCD_RETRY_ATTEMPTS) - printk("mcd: read of block %d failed\n", + printk(KERN_ERR "mcd: read of block %d failed\n", mcd_next_bn); #endif if (!McdTries--) { /* Nuts! This cd is ready for recycling! */ /* When WAS the last time YOU cleaned it CORRECTLY?! */ - printk - ("mcd: read of block %d failed, giving up\n", + printk(KERN_ERR "mcd: read of block %d failed, giving up\n", mcd_next_bn); if (mcd_transfer_is_active) { McdTries = 0; @@ -747,54 +701,35 @@ static void mcd_poll(unsigned long dummy) /* We ARE a double speed and we ARE bitching! */ if (mcd1xhold == 0) { /* Okay, Like are we STILL at single speed? *//* We need to switch back to double speed now... */ MCMD_DATA_READ = MCMD_2X_READ; /* Uhhh... BACK You GO! */ - printk("mcd: Switching back to 2X speed!\n"); /* Tell 'em! */ + printk(KERN_INFO "mcd: Switching back to 2X speed!\n"); /* Tell 'em! */ } else mcd1xhold--; /* No?! Count down the good reads some more... */ /* and try, try again! */ } - - - immediately: +immediately: switch (mcd_state) { - - - case MCD_S_IDLE: -#ifdef TEST3 - printk("MCD_S_IDLE\n"); -#endif + test3(printk("MCD_S_IDLE\n")); goto out; - - case MCD_S_START: -#ifdef TEST3 - printk("MCD_S_START\n"); -#endif - + test3(printk("MCD_S_START\n")); outb(MCMD_GET_STATUS, MCDPORT(0)); mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE; McdTimeout = 3000; break; - - case MCD_S_MODE: -#ifdef TEST3 - printk("MCD_S_MODE\n"); -#endif - + test3(printk("MCD_S_MODE\n")); if ((st = mcdStatus()) != -1) { - if (st & MST_DSK_CHG) { mcdDiskChanged = 1; tocUpToDate = 0; mcd_invalidate_buffers(); } - set_mode_immediately: - +set_mode_immediately: if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) { mcdDiskChanged = 1; tocUpToDate = 0; @@ -802,6 +737,7 @@ static void mcd_poll(unsigned long dummy) mcd_state = MCD_S_START; goto immediately; } + printk(KERN_INFO); printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n"); @@ -810,33 +746,24 @@ static void mcd_poll(unsigned long dummy) end_request(0); goto out; } - outb(MCMD_SET_MODE, MCDPORT(0)); outb(1, MCDPORT(0)); mcd_mode = 1; mcd_state = MCD_S_READ; McdTimeout = 3000; - } break; - - case MCD_S_READ: -#ifdef TEST3 - printk("MCD_S_READ\n"); -#endif - + test3(printk("MCD_S_READ\n")); if ((st = mcdStatus()) != -1) { - if (st & MST_DSK_CHG) { mcdDiskChanged = 1; tocUpToDate = 0; mcd_invalidate_buffers(); } - read_immediately: - +read_immediately: if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) { mcdDiskChanged = 1; tocUpToDate = 0; @@ -844,6 +771,7 @@ static void mcd_poll(unsigned long dummy) mcd_state = MCD_S_START; goto immediately; } + printk(KERN_INFO); printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n"); @@ -871,29 +799,20 @@ static void mcd_poll(unsigned long dummy) } break; - case MCD_S_DATA: -#ifdef TEST3 - printk("MCD_S_DATA\n"); -#endif - + test3(printk("MCD_S_DATA\n")); st = inb(MCDPORT(1)) & (MFL_STATUSorDATA); - data_immediately: -#ifdef TEST5 - printk("Status %02x\n", st); -#endif +data_immediately: + test5(printk("Status %02x\n", st)) switch (st) { - case MFL_DATA: #ifdef WARN_IF_READ_FAILURE if (McdTries == 5) - printk("mcd: read of block %d failed\n", + printk(KERN_WARNING "mcd: read of block %d failed\n", mcd_next_bn); #endif if (!McdTries--) { - printk - ("mcd: read of block %d failed, giving up\n", - mcd_next_bn); + printk(KERN_ERR "mcd: read of block %d failed, giving up\n", mcd_next_bn); if (mcd_transfer_is_active) { McdTries = 0; break; @@ -916,14 +835,12 @@ static void mcd_poll(unsigned long dummy) goto immediately; } mcd_buf_bn[mcd_buf_in] = -1; - READ_DATA(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, + insb(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, 2048); mcd_buf_bn[mcd_buf_in] = mcd_next_bn++; if (mcd_buf_out == -1) mcd_buf_out = mcd_buf_in; - mcd_buf_in = - mcd_buf_in + 1 == - MCD_BUF_SIZ ? 0 : mcd_buf_in + 1; + mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1; if (!mcd_transfer_is_active) { while (CURRENT_VALID) { mcd_transfer(); @@ -941,43 +858,23 @@ static void mcd_poll(unsigned long dummy) goto immediately; } McdTimeout = READ_TIMEOUT; -#ifdef DOUBLE_QUICK_ONLY - if (MCMD_DATA_READ != MCMD_PLAY_READ) -#endif { int count = QUICK_LOOP_COUNT; while (count--) { QUICK_LOOP_DELAY; - if ((st = - (inb(MCDPORT(1))) & - (MFL_STATUSorDATA)) != - (MFL_STATUSorDATA)) { -# ifdef TEST4 -/* printk("Quickloop success at %d\n",QUICK_LOOP_COUNT-count); */ - printk(" %d ", - QUICK_LOOP_COUNT - - count); -# endif + if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) { + test4(printk(" %d ", QUICK_LOOP_COUNT - count)); goto data_immediately; } } -# ifdef TEST4 -/* printk("Quickloop ended at %d\n",QUICK_LOOP_COUNT); */ - printk("ended "); -# endif + test4(printk("ended ")); } break; } break; - - case MCD_S_STOP: -#ifdef TEST3 - printk("MCD_S_STOP\n"); -#endif - -#ifdef WORK_AROUND_MITSUMI_BUG_93 + test3(printk("MCD_S_STOP\n")); if (!mitsumi_bug_93_wait) goto do_not_work_around_mitsumi_bug_93_1; @@ -989,41 +886,29 @@ static void mcd_poll(unsigned long dummy) if (McdTimeout) break; - do_not_work_around_mitsumi_bug_93_1: -#endif /* WORK_AROUND_MITSUMI_BUG_93 */ - +do_not_work_around_mitsumi_bug_93_1: outb(MCMD_STOP, MCDPORT(0)); - -#ifdef WORK_AROUND_MITSUMI_BUG_92 if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) { int i = 4096; do { inb(MCDPORT(0)); - } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == - MFL_STATUS && --i); + } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i); outb(MCMD_STOP, MCDPORT(0)); - if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == - MFL_STATUS) { + if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) { i = 4096; do { inb(MCDPORT(0)); - } while ((inb(MCDPORT(1)) & - MFL_STATUSorDATA) == MFL_STATUS - && --i); + } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i); outb(MCMD_STOP, MCDPORT(0)); } } -#endif /* WORK_AROUND_MITSUMI_BUG_92 */ mcd_state = MCD_S_STOPPING; McdTimeout = 1000; break; case MCD_S_STOPPING: -#ifdef TEST3 - printk("MCD_S_STOPPING\n"); -#endif - + test3(printk("MCD_S_STOPPING\n")); if ((st = mcdStatus()) == -1 && McdTimeout) break; @@ -1032,7 +917,6 @@ static void mcd_poll(unsigned long dummy) tocUpToDate = 0; mcd_invalidate_buffers(); } -#ifdef WORK_AROUND_MITSUMI_BUG_93 if (!mitsumi_bug_93_wait) goto do_not_work_around_mitsumi_bug_93_2; @@ -1043,17 +927,10 @@ static void mcd_poll(unsigned long dummy) case 9 + 3 + 2: if (McdTimeout) break; - st = -1; - do_not_work_around_mitsumi_bug_93_2: -#endif /* WORK_AROUND_MITSUMI_BUG_93 */ - -#ifdef TEST3 - printk("CURRENT_VALID %d mcd_mode %d\n", - CURRENT_VALID, mcd_mode); -#endif - +do_not_work_around_mitsumi_bug_93_2: + test3(printk("CURRENT_VALID %d mcd_mode %d\n", CURRENT_VALID, mcd_mode)); if (CURRENT_VALID) { if (st != -1) { if (mcd_mode == 1) @@ -1069,25 +946,21 @@ static void mcd_poll(unsigned long dummy) goto out; } break; - default: - printk("mcd: invalid state %d\n", mcd_state); + printk(KERN_ERR "mcd: invalid state %d\n", mcd_state); goto out; } - - ret: +ret: if (!McdTimeout--) { - printk("mcd: timeout in state %d\n", mcd_state); + printk(KERN_WARNING "mcd: timeout in state %d\n", mcd_state); mcd_state = MCD_S_STOP; } - - SET_TIMER(mcd_poll, 1); - out: + mcd_timer.function = mcd_poll; + mod_timer(&mcd_timer, jiffies + 1); +out: return; } - - static void mcd_invalidate_buffers(void) { int i; @@ -1096,7 +969,6 @@ static void mcd_invalidate_buffers(void) mcd_buf_out = -1; } - /* * Open the device special file. Check that a disk is in. */ @@ -1125,11 +997,11 @@ static int mcd_open(struct cdrom_device_info *cdi, int purpose) if (updateToc() < 0) goto err_out; - bump_count: +bump_count: ++mcd_open_count; return 0; - err_out: +err_out: MOD_DEC_USE_COUNT; return -EIO; } @@ -1140,10 +1012,10 @@ static int mcd_open(struct cdrom_device_info *cdi, int purpose) */ static void mcd_release(struct cdrom_device_info *cdi) { - MOD_DEC_USE_COUNT; if (!--mcd_open_count) { mcd_invalidate_buffers(); } + MOD_DEC_USE_COUNT; } @@ -1155,8 +1027,7 @@ static void cleanup(int level) switch (level) { case 3: if (unregister_cdrom(&mcd_info)) { - printk(KERN_WARNING - "Can't unregister cdrom mcd\n"); + printk(KERN_WARNING "Can't unregister cdrom mcd\n"); return; } free_irq(mcd_irq, NULL); @@ -1164,8 +1035,7 @@ static void cleanup(int level) release_region(mcd_port, 4); case 1: if (devfs_unregister_blkdev(MAJOR_NR, "mcd")) { - printk(KERN_WARNING - "Can't unregister major mcd\n"); + printk(KERN_WARNING "Can't unregister major mcd\n"); return; } blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); @@ -1186,19 +1056,17 @@ int __init mcd_init(void) char msg[80]; if (mcd_port <= 0 || mcd_irq <= 0) { - printk("skip mcd_init\n"); + printk(KERN_INFO "mcd: not probing.\n"); return -EIO; } if (devfs_register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0) { - printk("Unable to get major %d for Mitsumi CD-ROM\n", - MAJOR_NR); + printk(KERN_ERR "mcd: Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR); return -EIO; } if (check_region(mcd_port, 4)) { cleanup(1); - printk("Init failed, I/O port (%X) already in use\n", - mcd_port); + printk(KERN_ERR "mcd: Initialization failed, I/O port (%X) already in use\n", mcd_port); return -EIO; } @@ -1218,7 +1086,7 @@ int __init mcd_init(void) break; if (count >= 2000000) { - printk("Init failed. No mcd device at 0x%x irq %d\n", + printk(KERN_INFO "mcd: initialisation failed - No mcd device at 0x%x irq %d\n", mcd_port, mcd_irq); cleanup(1); return -EIO; @@ -1228,7 +1096,7 @@ int __init mcd_init(void) outb(MCMD_GET_VERSION, MCDPORT(0)); for (count = 0; count < 3; count++) if (getValue(result + count)) { - printk("mitsumi get version failed at 0x%x\n", + printk(KERN_ERR "mcd: mitsumi get version failed at 0x%x\n", mcd_port); cleanup(1); return -EIO; @@ -1246,10 +1114,8 @@ int __init mcd_init(void) /* don't get the IRQ until we know for sure the drive is there */ - if (request_irq - (mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) { - printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", - mcd_irq); + if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) { + printk(KERN_ERR "mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq); cleanup(1); return -EIO; } @@ -1282,7 +1148,7 @@ int __init mcd_init(void) mcd_info.dev = MKDEV(MAJOR_NR, 0); if (register_cdrom(&mcd_info) != 0) { - printk("Cannot register Mitsumi CD-ROM!\n"); + printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n"); cleanup(3); return -EIO; } @@ -1373,8 +1239,8 @@ static void mcdStatTimer(unsigned long dummy) wake_up(&mcd_waitq); return; } - - SET_TIMER(mcdStatTimer, 1); + mcd_timer.function = mcdStatTimer; + mod_timer(&mcd_timer, jiffies + 1); } @@ -1389,7 +1255,8 @@ static int getMcdStatus(int timeout) int st; McdTimeout = timeout; - SET_TIMER(mcdStatTimer, 1); + mcd_timer.function = mcdStatTimer; + mod_timer(&mcd_timer, jiffies + 1); sleep_on(&mcd_waitq); if (McdTimeout <= 0) return -1; @@ -1414,6 +1281,7 @@ static int getMcdStatus(int timeout) /* gives current state of the drive This function is quite unreliable, and should probably be rewritten by someone, eventually... */ + int mcd_drive_status(struct cdrom_device_info *cdi, int slot_nr) { int st; @@ -1456,7 +1324,6 @@ static int getValue(unsigned char *result) return 0; } - /* * Read the current Q-channel info. Also used for reading the * table of contents. @@ -1500,12 +1367,11 @@ int GetQChannelInfo(struct mcd_Toc *qp) return 0; } - /* * Read the table of contents (TOC) and TOC header if necessary */ -static int updateToc() +static int updateToc(void) { if (tocUpToDate) return 0; @@ -1520,12 +1386,11 @@ static int updateToc() return 0; } - /* * Read the table of contents header */ -static int GetDiskInfo() +static int GetDiskInfo(void) { int retry; @@ -1571,12 +1436,11 @@ static int GetDiskInfo() return 0; } - /* * Read the table of contents (TOC) */ -static int GetToc() +static int GetToc(void) { int i, px; int limit; @@ -1653,7 +1517,6 @@ static int GetToc() return limit > 0 ? 0 : -1; } - void __exit mcd_exit(void) { cleanup(3); diff --git a/drivers/cdrom/mcd.h b/drivers/cdrom/mcd.h index ce903d2f2f3d..1e36c4192bb0 100644 --- a/drivers/cdrom/mcd.h +++ b/drivers/cdrom/mcd.h @@ -70,17 +70,6 @@ /* borrowed from hd.c */ -#define READ_DATA(port, buf, nr) \ -insb(port, buf, nr) - -#define SET_TIMER(func, jifs) \ - do { \ - mcd_timer.function = func; \ - mod_timer(&mcd_timer, jiffies + jifs); \ - } while (0) - -#define CLEAR_TIMER del_timer_async(&mcd_timer); - #define MAX_TRACKS 104 struct msf { @@ -108,3 +97,10 @@ struct mcd_Toc { struct msf trackTime; struct msf diskTime; }; + +#define test1(x) +#define test2(x) +#define test3(x) +#define test4(x) +#define test5(x) + diff --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c index 233e8efd5fce..5be72a8e29aa 100644 --- a/drivers/cdrom/optcd.c +++ b/drivers/cdrom/optcd.c @@ -2097,3 +2097,4 @@ module_init(optcd_init); module_exit(optcd_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index 127771813a60..e8e50a8ee40b 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -4272,6 +4272,12 @@ static int sbpcd_dev_ioctl(struct cdrom_device_info *cdi, u_int cmd, D_S[d].aud_buf=NULL; D_S[d].sbp_audsiz=arg; + if (D_S[d].sbp_audsiz>16) + { + D_S[d].sbp_audsiz = 0; + RETURN_UP(D_S[d].sbp_audsiz); + } + if (D_S[d].sbp_audsiz>0) { D_S[d].aud_buf=(u_char *) vmalloc(D_S[d].sbp_audsiz*CD_FRAMESIZE_RAW); @@ -6016,6 +6022,9 @@ static int sbpcd_media_changed( struct cdrom_device_info *cdi, int disc_nr) { return sbpcd_chk_disk_change(cdi->dev); } + +MODULE_LICENSE("GPL"); + /*==========================================================================*/ /* * Overrides for Emacs so that we follow Linus's tabbing style. @@ -6033,3 +6042,4 @@ static int sbpcd_media_changed( struct cdrom_device_info *cdi, int disc_nr) * c-continued-brace-offset: 0 * End: */ + diff --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c index a66fb5651284..6d8e6c331ca2 100644 --- a/drivers/cdrom/sonycd535.c +++ b/drivers/cdrom/sonycd535.c @@ -1709,3 +1709,4 @@ module_init(sony535_init); module_exit(sony535_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 3ee4c527858e..ba9b9bc37631 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -59,6 +59,24 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION fi + if [ "$CONFIG_MIPS" = "y" ]; then + bool ' TMPTX3912/PR31700 serial port support' CONFIG_SERIAL_TX3912 + dep_bool ' Console on TMPTX3912/PR31700 serial port' CONFIG_SERIAL_TX3912_CONSOLE $CONFIG_SERIAL_TX3912 + bool ' Enable Au1000 UART Support' CONFIG_AU1000_UART + if [ "$CONFIG_AU1000_UART" = "y" ]; then + bool ' Enable Au1000 serial console' CONFIG_AU1000_SERIAL_CONSOLE + fi + fi +fi +if [ "$CONFIG_IT8712" = "y" ]; then + bool 'Enable Qtronix 990P Keyboard Support' CONFIG_QTRONIX_KEYBOARD + if [ "$CONFIG_QTRONIX_KEYBOARD" = "y" ]; then + define_bool CONFIG_IT8172_CIR y + else + bool ' Enable PS2 Keyboard Support' CONFIG_PC_KEYB + fi + bool 'Enable Smart Card Reader 0 Support ' CONFIG_IT8172_SCR0 + bool 'Enable Smart Card Reader 1 Support ' CONFIG_IT8172_SCR1 fi if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232 diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 4bce5b67caa8..f7f9d67746d6 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -41,6 +41,12 @@ ifeq ($(ARCH),s390) SERIAL = endif +ifeq ($(ARCH),mips) + ifneq ($(CONFIG_PC_KEYB),y) + KEYBD = + endif +endif + ifeq ($(ARCH),s390x) KEYMAP = KEYBD = @@ -95,8 +101,9 @@ ifeq ($(ARCH),sh) endif ifeq ($(CONFIG_DECSTATION),y) + KEYMAP = KEYBD = - SERIAL = + SERIAL = decserial.o endif ifeq ($(CONFIG_BAGET_MIPS),y) @@ -104,10 +111,18 @@ ifeq ($(CONFIG_BAGET_MIPS),y) SERIAL = endif +ifeq ($(CONFIG_NINO),y) + SERIAL = +endif + ifneq ($(CONFIG_SUN_SERIAL),) SERIAL = endif +ifeq ($(CONFIG_QTRONIX_KEYBOARD),y) + KEYBD = qtronix.o + KEYMAP = qtronixmap.o +endif obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o obj-$(CONFIG_SERIAL) += $(SERIAL) @@ -147,6 +162,7 @@ obj-$(CONFIG_SERIAL167) += serial167.o obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o +obj-$(CONFIG_SERIAL_TX3912) += generic_serial.o serial_tx3912.o subdir-$(CONFIG_RIO) += rio subdir-$(CONFIG_INPUT) += joystick @@ -231,3 +247,6 @@ consolemap_deftbl.o: consolemap_deftbl.c $(TOPDIR)/include/linux/types.h defkeymap.c: defkeymap.map set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ + +qtronixmap.c: qtronixmap.map + set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ diff --git a/drivers/char/decserial.c b/drivers/char/decserial.c new file mode 100644 index 000000000000..6c82b6cf816c --- /dev/null +++ b/drivers/char/decserial.c @@ -0,0 +1,98 @@ +/* + * sercons.c + * choose the right serial device at boot time + * + * triemer 6-SEP-1998 + * sercons.c is designed to allow the three different kinds + * of serial devices under the decstation world to co-exist + * in the same kernel. The idea here is to abstract + * the pieces of the drivers that are common to this file + * so that they do not clash at compile time and runtime. + * + * HK 16-SEP-1998 v0.002 + * removed the PROM console as this is not a real serial + * device. Added support for PROM console in drivers/char/tty_io.c + * instead. Although it may work to enable more than one + * console device I strongly recommend to use only one. + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <asm/dec/machtype.h> + +#ifdef CONFIG_ZS +extern int zs_init(void); +#endif + +#ifdef CONFIG_DZ +extern int dz_init(void); +#endif + +#ifdef CONFIG_SERIAL_CONSOLE + +#ifdef CONFIG_ZS +extern void zs_serial_console_init(void); +#endif + +#ifdef CONFIG_DZ +extern void dz_serial_console_init(void); +#endif + +#endif + +/* rs_init - starts up the serial interface - + handle normal case of starting up the serial interface */ + +#ifdef CONFIG_SERIAL + +int __init rs_init(void) +{ + +#if defined(CONFIG_ZS) && defined(CONFIG_DZ) + if (IOASIC) + return zs_init(); + else + return dz_init(); +#else + +#ifdef CONFIG_ZS + return zs_init(); +#endif + +#ifdef CONFIG_DZ + return dz_init(); +#endif + +#endif +} + +__initcall(rs_init); + +#endif + +#ifdef CONFIG_SERIAL_CONSOLE + +/* serial_console_init handles the special case of starting + * up the console on the serial port + */ +void __init serial_console_init(void) +{ +#if defined(CONFIG_ZS) && defined(CONFIG_DZ) + if (IOASIC) + zs_serial_console_init(); + else + dz_serial_console_init(); +#else + +#ifdef CONFIG_ZS + zs_serial_console_init(); +#endif + +#ifdef CONFIG_DZ + dz_serial_console_init(); +#endif + +#endif +} + +#endif diff --git a/drivers/char/dz.c b/drivers/char/dz.c index 9104b838d41d..431e0fb34b29 100644 --- a/drivers/char/dz.c +++ b/drivers/char/dz.c @@ -21,15 +21,10 @@ Qua Jun 27 15:02:26 BRT 2001 * [07-SEP-99] Bugfixes */ -#define DEBUG_DZ 1 +/* #define DEBUG_DZ 1 */ -#ifdef MODULE -#include <linux/module.h> #include <linux/version.h> -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif +#include <linux/module.h> #include <linux/config.h> #include <linux/kernel.h> @@ -41,9 +36,10 @@ Qua Jun 27 15:02:26 BRT 2001 #include <linux/param.h> #include <linux/tqueue.h> #include <linux/interrupt.h> +#include <linux/serial.h> +#include <linux/serialP.h> #include <asm-mips/wbflush.h> -/* for definition of SERIAL */ -#include <asm/dec/interrupts.h> +#include <asm/dec/interrupts.h> /* for definition of SERIAL */ /* for definition of struct console */ #ifdef CONFIG_SERIAL_CONSOLE @@ -91,13 +87,13 @@ static unsigned char tmp_buffer[256]; */ static void debug_console( const char *s,int count) { - unsigned i; + unsigned i; - for (i = 0; i < count; i++) { - if (*s == 10) - prom_printf("%c", 13); - prom_printf("%c", *s++); - } + for (i = 0; i < count; i++) { + if (*s == 10) + prom_printf("%c", 13); + prom_printf("%c", *s++); + } } #endif @@ -112,16 +108,16 @@ static void debug_console( const char *s,int count) static inline unsigned short dz_in (struct dz_serial *info, unsigned offset) { - volatile unsigned short *addr = (volatile unsigned short *)(info->port + offset); - return *addr; + volatile u16 *addr = (volatile u16 *)(info->port + offset); + + return *addr; } -static inline void dz_out (struct dz_serial *info, unsigned offset, unsigned short value) +static inline void dz_out (struct dz_serial *info, unsigned offset, + unsigned short value) { - - volatile unsigned short *addr = (volatile unsigned short *)(info->port + offset); - *addr = value; - + volatile u16 *addr = (volatile u16 *)(info->port + offset); + *addr = value; } /* @@ -136,33 +132,32 @@ static inline void dz_out (struct dz_serial *info, unsigned offset, unsigned sho static void dz_stop (struct tty_struct *tty) { - struct dz_serial *info; - unsigned short mask, tmp; + struct dz_serial *info; + unsigned short mask, tmp; - if (tty==0) - return; + if (!tty) + return; - info = (struct dz_serial *)tty->driver_data; - - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); /* read the TX flag */ + info = (struct dz_serial *)tty->driver_data; + + mask = 1 << info->line; + tmp = dz_in (info, DZ_TCR); /* read the TX flag */ - tmp &= ~mask; /* clear the TX flag */ - dz_out (info, DZ_TCR, tmp); -} + tmp &= ~mask; /* clear the TX flag */ + dz_out (info, DZ_TCR, tmp); +} static void dz_start (struct tty_struct *tty) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned short mask, tmp; - - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); /* read the TX flag */ + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + unsigned short mask, tmp; - tmp |= mask; /* set the TX flag */ - dz_out (info, DZ_TCR, tmp); + mask = 1 << info->line; + tmp = dz_in (info, DZ_TCR); /* read the TX flag */ -} + tmp |= mask; /* set the TX flag */ + dz_out (info, DZ_TCR, tmp); +} /* * ------------------------------------------------------------ @@ -194,9 +189,9 @@ static void dz_start (struct tty_struct *tty) */ static inline void dz_sched_event (struct dz_serial *info, int event) { - info->event |= 1 << event; - queue_task (&info->tqueue, &tq_serial); - mark_bh (SERIAL_BH); + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); } /* @@ -208,98 +203,101 @@ static inline void dz_sched_event (struct dz_serial *info, int event) */ static inline void receive_chars (struct dz_serial *info_in) { + struct dz_serial *info; + struct tty_struct *tty = 0; + struct async_icount *icount; + int ignore = 0; + unsigned short status, tmp; + unsigned char ch; - struct dz_serial *info; - struct tty_struct *tty = 0; - struct async_icount *icount; - int ignore = 0; - unsigned short status, tmp; - unsigned char ch; - - /* this code is going to be a problem... - the call to tty_flip_buffer is going to need - to be rethought... - */ - do - { - status = dz_in (info_in, DZ_RBUF); - info = lines[LINE(status)]; - - /* punt so we don't get duplicate characters */ - if (!(status & DZ_DVAL)) - goto ignore_char; + /* + * This code is going to be a problem... the call to tty_flip_buffer + * is going to need to be rethought... + */ + do { + status = dz_in (info_in, DZ_RBUF); + info = lines[LINE(status)]; + /* punt so we don't get duplicate characters */ + if (!(status & DZ_DVAL)) + goto ignore_char; - ch = UCHAR(status); /* grab the char */ + ch = UCHAR(status); /* grab the char */ #if 0 - if (info->is_console) { - if (ch == 0) return; /* it's a break ... */ + if (info->is_console) { + if (ch == 0) + return; /* it's a break ... */ - wake_up (&keypress_wait); /* It is a 'keyboard interrupt' ;-) */ - } + wake_up (&keypress_wait); /* It is a 'keyboard interrupt' ;-) */ + } #endif - tty = info->tty; /* now tty points to the proper dev */ - icount = &info->icount; - - if (!tty) break; - if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; - - *tty->flip.char_buf_ptr = ch; - *tty->flip.flag_buf_ptr = 0; - icount->rx++; - - /* keep track of the statistics */ - if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { - if (status & DZ_PERR) /* parity error */ - icount->parity++; - else if (status & DZ_FERR) /* frame error */ - icount->frame++; - if (status & DZ_OERR) /* overrun error */ - icount->overrun++; - - /* check to see if we should ignore the character - and mask off conditions that should be ignored - */ - - if (status & info->ignore_status_mask) { - if (++ignore > 100 ) break; - goto ignore_char; - } - - /* mask off the error conditions we want to ignore */ - tmp = status & info->read_status_mask; - - if (tmp & DZ_PERR) - { - *tty->flip.flag_buf_ptr = TTY_PARITY; - debug_console("PERR\n",5); - } - else if (tmp & DZ_FERR) - { - *tty->flip.flag_buf_ptr = TTY_FRAME; - debug_console("FERR\n",5); - } - if (tmp & DZ_OERR) - { - debug_console("OERR\n",5); - if (tty->flip.count < TTY_FLIPBUF_SIZE) { - tty->flip.count++; - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - *tty->flip.flag_buf_ptr = TTY_OVERRUN; - } - } - } - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - tty->flip.count++; - ignore_char: - } while (status & DZ_DVAL); - - if (tty) - tty_flip_buffer_push(tty); + tty = info->tty; /* now tty points to the proper dev */ + icount = &info->icount; + + if (!tty) + break; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; + + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = 0; + icount->rx++; + + /* keep track of the statistics */ + if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { + if (status & DZ_PERR) /* parity error */ + icount->parity++; + else if (status & DZ_FERR) /* frame error */ + icount->frame++; + if (status & DZ_OERR) /* overrun error */ + icount->overrun++; + + /* + * Check to see if we should ignore the character and + * mask off conditions that should be ignored + */ + + if (status & info->ignore_status_mask) { + if (++ignore > 100) + break; + goto ignore_char; + } + + /* mask off the error conditions we want to ignore */ + tmp = status & info->read_status_mask; + + if (tmp & DZ_PERR) { + *tty->flip.flag_buf_ptr = TTY_PARITY; +#ifdef DEBUG_DZ + debug_console("PERR\n",5); +#endif /* DEBUG_DZ */ + } else if (tmp & DZ_FERR) { + *tty->flip.flag_buf_ptr = TTY_FRAME; +#ifdef DEBUG_DZ + debug_console("FERR\n",5); +#endif /* DEBUG_DZ */ + } if (tmp & DZ_OERR) { +#ifdef DEBUG_DZ + debug_console("OERR\n",5); +#endif /* DEBUG_DZ */ + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } + } + } + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; +ignore_char: + ; + } while (status & DZ_DVAL); + + if (tty) + tty_flip_buffer_push(tty); } /* @@ -311,35 +309,37 @@ static inline void receive_chars (struct dz_serial *info_in) */ static inline void transmit_chars (struct dz_serial *info) { - unsigned char tmp; - - + unsigned char tmp; - if (info->x_char) { /* XON/XOFF chars */ - dz_out (info, DZ_TDR, info->x_char); - info->icount.tx++; - info->x_char = 0; - return; - } - - /* if nothing to do or stopped or hardware stopped */ - if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped) { - dz_stop (info->tty); - return; - } + if (info->x_char) { /* XON/XOFF chars */ + dz_out(info, DZ_TDR, info->x_char); + info->icount.tx++; + info->x_char = 0; + return; + } - /* if something to do ... (rember the dz has no output fifo so we go one char at a time :-< */ - tmp = (unsigned short)info->xmit_buf[info->xmit_tail++]; - dz_out (info, DZ_TDR, tmp); - info->xmit_tail = info->xmit_tail & (DZ_XMIT_SIZE - 1); - info->icount.tx++; + /* if nothing to do or stopped or hardware stopped */ + if ((info->xmit_cnt <= 0) || info->tty->stopped || + info->tty->hw_stopped) { + dz_stop(info->tty); + return; + } - if (--info->xmit_cnt < WAKEUP_CHARS) - dz_sched_event (info, DZ_EVENT_WRITE_WAKEUP); + /* + * If something to do ... (rember the dz has no output fifo so we go + * one char at a time :-< + */ + tmp = (unsigned short) info->xmit_buf[info->xmit_tail++]; + dz_out(info, DZ_TDR, tmp); + info->xmit_tail = info->xmit_tail & (DZ_XMIT_SIZE - 1); + info->icount.tx++; + if (--info->xmit_cnt < WAKEUP_CHARS) + dz_sched_event(info, DZ_EVENT_WRITE_WAKEUP); - /* Are we done */ - if (info->xmit_cnt <= 0) dz_stop (info->tty); + /* Are we done */ + if (info->xmit_cnt <= 0) + dz_stop(info->tty); } /* @@ -351,15 +351,17 @@ static inline void transmit_chars (struct dz_serial *info) */ static inline void check_modem_status (struct dz_serial *info) { - unsigned short status; + unsigned short status; - /* if not ne modem line just return */ - if (info->line != DZ_MODEM) return; + /* if not ne modem line just return */ + if (info->line != DZ_MODEM) + return; - status = dz_in (info, DZ_MSR); + status = dz_in(info, DZ_MSR); - /* it's easy, since DSR2 is the only bit in the register */ - if (status) info->icount.dsr++; + /* it's easy, since DSR2 is the only bit in the register */ + if (status) + info->icount.dsr++; } /* @@ -372,17 +374,18 @@ static inline void check_modem_status (struct dz_serial *info) */ static void dz_interrupt (int irq, void *dev, struct pt_regs *regs) { - struct dz_serial *info; - unsigned short status; + struct dz_serial *info; + unsigned short status; - status = dz_in ((struct dz_serial *)dev, DZ_CSR); /* get the reason why we just got an irq */ - info = lines[LINE(status)]; /* re-arrange info the proper port */ + /* get the reason why we just got an irq */ + status = dz_in((struct dz_serial *)dev, DZ_CSR); + info = lines[LINE(status)]; /* re-arrange info the proper port */ - if (status & DZ_RDONE) - receive_chars (info); /* the receive function */ + if (status & DZ_RDONE) + receive_chars(info); /* the receive function */ - if (status & DZ_TRDY) - transmit_chars (info); + if (status & DZ_TRDY) + transmit_chars (info); } /* @@ -402,21 +405,23 @@ static void dz_interrupt (int irq, void *dev, struct pt_regs *regs) */ static void do_serial_bh (void) { - run_task_queue (&tq_serial); + run_task_queue (&tq_serial); } static void do_softint (void *private_data) { - struct dz_serial *info = (struct dz_serial *)private_data; - struct tty_struct *tty = info->tty; + struct dz_serial *info = (struct dz_serial *) private_data; + struct tty_struct *tty = info->tty; - if (!tty) return; + if (!tty) + return; - if (test_and_clear_bit (DZ_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); - wake_up_interruptible (&tty->write_wait); - } + if (test_and_clear_bit(DZ_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible (&tty->write_wait); + } } /* @@ -431,12 +436,13 @@ static void do_softint (void *private_data) */ static void do_serial_hangup (void *private_data) { - struct dz_serial *info = (struct dz_serial *)private_data; - struct tty_struct *tty = info->tty;; + struct dz_serial *info = (struct dz_serial *) private_data; + struct tty_struct *tty = info->tty;; - if (!tty) return; + if (!tty) + return; - tty_hangup (tty); + tty_hangup(tty); } /* @@ -448,57 +454,52 @@ static void do_serial_hangup (void *private_data) */ static int startup (struct dz_serial *info) { - unsigned long page, flags; - unsigned short tmp; + unsigned long page, flags; + unsigned short tmp; - if (info->is_initialized) return 0; + if (info->is_initialized) + return 0; - save_flags (flags); - cli (); - - if (!info->port) { - if (info->tty) set_bit (TTY_IO_ERROR, &info->tty->flags); - restore_flags (flags); - return -ENODEV; - } - - if (!info->xmit_buf) { - page = get_free_page (GFP_KERNEL); - if (!page) { - restore_flags (flags); - return -ENOMEM; - } - info->xmit_buf = (unsigned char *)page; - } - - if (info->tty) clear_bit (TTY_IO_ERROR, &info->tty->flags); - - /* enable the interrupt and the scanning */ - tmp = dz_in (info, DZ_CSR); - tmp |= (DZ_RIE | DZ_TIE | DZ_MSE); - dz_out (info, DZ_CSR, tmp); - - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - /* set up the speed */ - change_speed (info); - - /* clear the line transmitter buffer - I can't figure out why I need to do this - but - its necessary - in order for the console portion - and the interrupt portion to live happily side by side. - */ - - /* clear the line transmitter buffer - I can't figure out why I need to do this - but - its necessary - in order for the console portion - and the interrupt portion to live happily side by side. - */ - - info->is_initialized = 1; - - restore_flags (flags); - return 0; + save_and_cli(flags); + + if (!info->port) { + if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); + restore_flags(flags); + return -ENODEV; + } + + if (!info->xmit_buf) { + page = get_free_page(GFP_KERNEL); + if (!page) { + restore_flags (flags); + return -ENOMEM; + } + info->xmit_buf = (unsigned char *)page; + } + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + + /* enable the interrupt and the scanning */ + tmp = dz_in(info, DZ_CSR); + tmp |= (DZ_RIE | DZ_TIE | DZ_MSE); + dz_out(info, DZ_CSR, tmp); + + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + change_speed(info); /* set up the speed */ + + /* + * Clear the line transmitter buffer I can't figure out why I need to + * do this - but its necessary - in order for the console portion and + * the interrupt portion to live happily side by side. + */ + + info->is_initialized = 1; + + restore_flags(flags); + + return 0; } /* @@ -511,38 +512,38 @@ static int startup (struct dz_serial *info) */ static void shutdown (struct dz_serial *info) { - unsigned long flags; - unsigned short tmp; + unsigned long flags; + unsigned short tmp; - if (!info->is_initialized) return; + if (!info->is_initialized) + return; - save_flags (flags); - cli (); + save_and_cli(flags); - dz_stop (info->tty); + dz_stop (info->tty); + info->cflags &= ~DZ_CREAD; /* turn off receive enable flag */ + dz_out(info, DZ_LPR, info->cflags); + if (info->xmit_buf) { /* free Tx buffer */ + free_page((unsigned long)info->xmit_buf); + info->xmit_buf = 0; + } - info->cflags &= ~DZ_CREAD; /* turn off receive enable flag */ - dz_out (info, DZ_LPR, info->cflags); - - if (info->xmit_buf) { /* free Tx buffer */ - free_page ((unsigned long)info->xmit_buf); - info->xmit_buf = 0; - } + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + tmp = dz_in(info, DZ_TCR); + if (tmp & DZ_MODEM_DTR) { + tmp &= ~DZ_MODEM_DTR; + dz_out(info, DZ_TCR, tmp); + } + } - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { - tmp = dz_in (info, DZ_TCR); - if (tmp & DZ_MODEM_DTR) { - tmp &= ~DZ_MODEM_DTR; - dz_out (info, DZ_TCR, tmp); - } - } + if (info->tty) + set_bit (TTY_IO_ERROR, &info->tty->flags); - if (info->tty) set_bit (TTY_IO_ERROR, &info->tty->flags); + info->is_initialized = 0; - info->is_initialized = 0; - restore_flags (flags); + restore_flags (flags); } /* @@ -554,65 +555,104 @@ static void shutdown (struct dz_serial *info) */ static void change_speed (struct dz_serial *info) { - unsigned long flags; - unsigned cflag; - int baud; + unsigned long flags; + unsigned cflag; + int baud; - if (!info->tty || !info->tty->termios) return; + if (!info->tty || !info->tty->termios) + return; - save_flags (flags); - cli (); + save_and_cli(flags); - info->cflags = info->line; + info->cflags = info->line; + + cflag = info->tty->termios->c_cflag; + + switch (cflag & CSIZE) { + case CS5: + info->cflags |= DZ_CS5; + break; + case CS6: + info->cflags |= DZ_CS6; + break; + case CS7: + info->cflags |= DZ_CS7; + break; + case CS8: + default: + info->cflags |= DZ_CS8; + } - cflag = info->tty->termios->c_cflag; + if (cflag & CSTOPB) + info->cflags |= DZ_CSTOPB; + if (cflag & PARENB) + info->cflags |= DZ_PARENB; + if (cflag & PARODD) + info->cflags |= DZ_PARODD; + + baud = tty_get_baud_rate(info->tty); + switch (baud) { + case 50: + info->cflags |= DZ_B50; + break; + case 75: + info->cflags |= DZ_B75; + break; + case 110: + info->cflags |= DZ_B110; + break; + case 134: + info->cflags |= DZ_B134; + break; + case 150: + info->cflags |= DZ_B150; + break; + case 300: + info->cflags |= DZ_B300; + break; + case 600: + info->cflags |= DZ_B600; + break; + case 1200: + info->cflags |= DZ_B1200; + break; + case 1800: + info->cflags |= DZ_B1800; + break; + case 2000: + info->cflags |= DZ_B2000; + break; + case 2400: + info->cflags |= DZ_B2400; + break; + case 3600: + info->cflags |= DZ_B3600; + break; + case 4800: + info->cflags |= DZ_B4800; + break; + case 7200: + info->cflags |= DZ_B7200; + break; + case 9600: + default: + info->cflags |= DZ_B9600; + } - switch (cflag & CSIZE) { - case CS5: info->cflags |= DZ_CS5; break; - case CS6: info->cflags |= DZ_CS6; break; - case CS7: info->cflags |= DZ_CS7; break; - case CS8: - default: info->cflags |= DZ_CS8; - } + info->cflags |= DZ_RXENAB; + dz_out(info, DZ_LPR, info->cflags); - if (cflag & CSTOPB) info->cflags |= DZ_CSTOPB; - if (cflag & PARENB) info->cflags |= DZ_PARENB; - if (cflag & PARODD) info->cflags |= DZ_PARODD; - - baud = tty_get_baud_rate (info->tty); - switch (baud) { - case 50 : info->cflags |= DZ_B50; break; - case 75 : info->cflags |= DZ_B75; break; - case 110 : info->cflags |= DZ_B110; break; - case 134 : info->cflags |= DZ_B134; break; - case 150 : info->cflags |= DZ_B150; break; - case 300 : info->cflags |= DZ_B300; break; - case 600 : info->cflags |= DZ_B600; break; - case 1200: info->cflags |= DZ_B1200; break; - case 1800: info->cflags |= DZ_B1800; break; - case 2000: info->cflags |= DZ_B2000; break; - case 2400: info->cflags |= DZ_B2400; break; - case 3600: info->cflags |= DZ_B3600; break; - case 4800: info->cflags |= DZ_B4800; break; - case 7200: info->cflags |= DZ_B7200; break; - case 9600: - default : info->cflags |= DZ_B9600; - } - - info->cflags |= DZ_RXENAB; - dz_out (info, DZ_LPR, info->cflags); - - /* setup accept flag */ - info->read_status_mask = DZ_OERR; - if (I_INPCK(info->tty)) - info->read_status_mask |= (DZ_FERR | DZ_PERR); + /* setup accept flag */ + info->read_status_mask = DZ_OERR; + if (I_INPCK(info->tty)) + info->read_status_mask |= (DZ_FERR | DZ_PERR); - /* characters to ignore */ - info->ignore_status_mask = 0; - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= (DZ_FERR | DZ_PERR); + /* characters to ignore */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= (DZ_FERR | DZ_PERR); - restore_flags (flags); + restore_flags(flags); } /* @@ -624,18 +664,16 @@ static void change_speed (struct dz_serial *info) */ static void dz_flush_chars (struct tty_struct *tty) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned long flags; - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) - return; + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + unsigned long flags; - save_flags (flags); - cli (); + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; - dz_start (info->tty); - - restore_flags (flags); + save_and_cli(flags); + dz_start (info->tty); + restore_flags(flags); } @@ -646,83 +684,81 @@ static void dz_flush_chars (struct tty_struct *tty) * main output routine. * ------------------------------------------------------------------- */ -static int dz_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +static int dz_write (struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned long flags; - int c, ret = 0; - - if (!tty ) return ret; - if (!info->xmit_buf) return ret; - if (!tmp_buf) tmp_buf = tmp_buffer; - - - - if (from_user) { - - down (&tmp_buf_sem); - while (1) { - c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); - if (c <= 0) break; - - c -= copy_from_user (tmp_buf, buf, c); - if (!c) { - if (!ret) ret = -EFAULT; - break; - } - - save_flags (flags); - cli (); - - c = MIN(c, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); - memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - info->xmit_head = ((info->xmit_head + c) & (DZ_XMIT_SIZE-1)); - info->xmit_cnt += c; - - restore_flags(flags); - - buf += c; - count -= c; - ret += c; - } - - up (&tmp_buf_sem); - } else { - - - while (1) { - save_flags (flags); - cli (); - - c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); - if (c <= 0) { - restore_flags (flags); - break; - } - memcpy (info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = ((info->xmit_head + c) & (DZ_XMIT_SIZE-1)); - info->xmit_cnt += c; - - restore_flags (flags); - - buf += c; - count -= c; - ret += c; - } - } - + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + unsigned long flags; + int c, ret = 0; + + if (!tty ) + return ret; + if (!info->xmit_buf) + return ret; + if (!tmp_buf) + tmp_buf = tmp_buffer; + + if (from_user) { + down (&tmp_buf_sem); + while (1) { + c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, + DZ_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + c -= copy_from_user (tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + + save_and_cli(flags); + + c = MIN(c, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, + DZ_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + info->xmit_head = ((info->xmit_head + c) & + (DZ_XMIT_SIZE - 1)); + info->xmit_cnt += c; + restore_flags(flags); + + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + while (1) { + save_and_cli(flags); + + c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, + DZ_XMIT_SIZE - info->xmit_head)); + if (c <= 0) { + restore_flags (flags); + break; + } + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = ((info->xmit_head + c) & + (DZ_XMIT_SIZE-1)); + info->xmit_cnt += c; + restore_flags(flags); + + buf += c; + count -= c; + ret += c; + } + } - if (info->xmit_cnt) - { - if (!tty->stopped) - { - if (!tty->hw_stopped) - { - dz_start (info->tty); - } + if (info->xmit_cnt) { + if (!tty->stopped) { + if (!tty->hw_stopped) { + dz_start (info->tty); + } + } } - } - return ret; + + return ret; } /* @@ -734,12 +770,14 @@ static int dz_write (struct tty_struct *tty, int from_user, const unsigned char */ static int dz_write_room (struct tty_struct *tty) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - int ret; - - ret = DZ_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) ret = 0; - return ret; + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + int ret; + + ret = DZ_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + + return ret; } /* @@ -751,9 +789,9 @@ static int dz_write_room (struct tty_struct *tty) */ static int dz_chars_in_buffer (struct tty_struct *tty) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; + struct dz_serial *info = (struct dz_serial *)tty->driver_data; - return info->xmit_cnt; + return info->xmit_cnt; } /* @@ -765,16 +803,17 @@ static int dz_chars_in_buffer (struct tty_struct *tty) */ static void dz_flush_buffer (struct tty_struct *tty) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; + struct dz_serial *info = (struct dz_serial *)tty->driver_data; - cli (); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - sti (); + cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + sti(); - wake_up_interruptible (&tty->write_wait); + wake_up_interruptible (&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + tty->ldisc.write_wakeup(tty); } /* @@ -787,31 +826,32 @@ static void dz_flush_buffer (struct tty_struct *tty) */ static void dz_throttle (struct tty_struct *tty) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; + struct dz_serial *info = (struct dz_serial *)tty->driver_data; - if (I_IXOFF(tty)) - info->x_char = STOP_CHAR(tty); + if (I_IXOFF(tty)) + info->x_char = STOP_CHAR(tty); } static void dz_unthrottle (struct tty_struct *tty) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - info->x_char = START_CHAR(tty); - } + struct dz_serial *info = (struct dz_serial *)tty->driver_data; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + info->x_char = START_CHAR(tty); + } } static void dz_send_xchar (struct tty_struct *tty, char ch) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; + struct dz_serial *info = (struct dz_serial *)tty->driver_data; - info->x_char = ch; + info->x_char = ch; - if (ch) dz_start (info->tty); + if (ch) + dz_start(info->tty); } /* @@ -819,59 +859,62 @@ static void dz_send_xchar (struct tty_struct *tty, char ch) * rs_ioctl () and friends * ------------------------------------------------------------ */ -static int get_serial_info (struct dz_serial *info, struct serial_struct *retinfo) +static int get_serial_info(struct dz_serial *info, + struct serial_struct *retinfo) { - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - - memset (&tmp, 0, sizeof(tmp)); - - tmp.type = info->type; - tmp.line = info->line; - tmp.port = info->port; - tmp.irq = SERIAL; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + struct serial_struct tmp; - return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; + if (!retinfo) + return -EFAULT; + + memset (&tmp, 0, sizeof(tmp)); + + tmp.type = info->type; + tmp.line = info->line; + tmp.port = info->port; + tmp.irq = SERIAL; + tmp.flags = info->flags; + tmp.baud_base = info->baud_base; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + + return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; } -static int set_serial_info (struct dz_serial *info, struct serial_struct *new_info) +static int set_serial_info (struct dz_serial *info, + struct serial_struct *new_info) { - struct serial_struct new_serial; - struct dz_serial old_info; - int retval = 0; + struct serial_struct new_serial; + struct dz_serial old_info; + int retval = 0; - if (!new_info) - return -EFAULT; + if (!new_info) + return -EFAULT; - if(copy_from_user (&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - - old_info = *info; + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + old_info = *info; - if (info->count > 1) - return -EBUSY; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud_base = new_serial.baud_base; - info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; + if (info->count > 1) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ - retval = startup (info); - return retval; + info->baud_base = new_serial.baud_base; + info->type = new_serial.type; + info->close_delay = new_serial.close_delay; + info->closing_wait = new_serial.closing_wait; + + retval = startup(info); + + return retval; } /* @@ -886,9 +929,9 @@ static int set_serial_info (struct dz_serial *info, struct serial_struct *new_in */ static int get_lsr_info (struct dz_serial *info, unsigned int *value) { - unsigned short status = dz_in (info, DZ_LPR); + unsigned short status = dz_in (info, DZ_LPR); - return put_user (status, value); + return put_user (status, value); } /* @@ -896,106 +939,112 @@ static int get_lsr_info (struct dz_serial *info, unsigned int *value) */ static void send_break (struct dz_serial *info, int duration) { - unsigned long flags; - unsigned short tmp, mask; + unsigned long flags; + unsigned short tmp, mask; - if (!info->port) - return; + if (!info->port) + return; - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); - tmp |= mask; + mask = 1 << info->line; + tmp = dz_in (info, DZ_TCR); + tmp |= mask; - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_INTERRUPTIBLE; - save_flags (flags); - cli(); - - dz_out (info, DZ_TCR, tmp); - - schedule_timeout(duration); - - tmp &= ~mask; - dz_out (info, DZ_TCR, tmp); - - restore_flags (flags); + save_and_cli(flags); + dz_out(info, DZ_TCR, tmp); + schedule_timeout(duration); + tmp &= ~mask; + dz_out(info, DZ_TCR, tmp); + restore_flags(flags); } -static int dz_ioctl (struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) +static int dz_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { - struct dz_serial * info = (struct dz_serial *)tty->driver_data; - int retval; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && - (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change (tty); - if (retval) - return retval; - tty_wait_until_sent (tty, 0); - if (!arg) - send_break (info, HZ/4); /* 1/4 second */ - return 0; - - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change (tty); - if (retval) - return retval; - tty_wait_until_sent (tty, 0); - send_break (info, arg ? arg*(HZ/10) : HZ/4); - return 0; - - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); - - case TIOCSSOFTCAR: - if (get_user (arg, (unsigned long *)arg)) - return -EFAULT; - tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); - return 0; - - case TIOCGSERIAL: - return get_serial_info (info, (struct serial_struct *)arg); - - case TIOCSSERIAL: - return set_serial_info (info, (struct serial_struct *) arg); - - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info (info, (unsigned int *)arg); - - case TIOCSERGSTRUCT: - return copy_to_user((struct dz_serial *)arg, info, - sizeof(struct dz_serial)) ? -EFAULT : 0; - - default: - return -ENOIOCTLCMD; - } + int error; + struct dz_serial * info = (struct dz_serial *)tty->driver_data; + int retval; + + if (cmd != TIOCGSERIAL && cmd != TIOCSSERIAL && + cmd != TIOCSERCONFIG && cmd != TIOCSERGWILD && + cmd != TIOCSERSWILD && cmd != TIOCSERGSTRUCT) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } - return 0; + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + send_break(info, HZ/4); /* 1/4 second */ + return 0; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + send_break(info, arg ? arg*(HZ/10) : HZ/4); + return 0; + + case TIOCGSOFTCAR: + error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); + return 0; + + case TIOCSSOFTCAR: + if (get_user (arg, (unsigned long *)arg)) + return -EFAULT; + + tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0); + return 0; + + case TIOCGSERIAL: + error = verify_area(VERIFY_WRITE, (void *)arg, + sizeof(struct serial_struct)); + if (error) + return error; + return get_serial_info(info, (struct serial_struct *)arg); + + case TIOCSSERIAL: + return set_serial_info(info, (struct serial_struct *) arg); + + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info (info, (unsigned int *)arg); + + case TIOCSERGSTRUCT: + return copy_to_user((struct dz_serial *)arg, info, + sizeof(struct dz_serial)) ? -EFAULT : 0; + + default: + return -ENOIOCTLCMD; + } + + return 0; } static void dz_set_termios (struct tty_struct *tty, struct termios *old_termios) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; + struct dz_serial *info = (struct dz_serial *)tty->driver_data; - if (tty->termios->c_cflag == old_termios->c_cflag) - return; + if (tty->termios->c_cflag == old_termios->c_cflag) + return; - change_speed (info); + change_speed (info); - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - dz_start (tty); - } + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + dz_start(tty); + } } /* @@ -1007,97 +1056,95 @@ static void dz_set_termios (struct tty_struct *tty, * the transmit enable and receive enable flags. * ------------------------------------------------------------ */ -static void dz_close (struct tty_struct *tty, struct file *filp) +static void dz_close(struct tty_struct *tty, struct file *filp) { - struct dz_serial * info = (struct dz_serial *)tty->driver_data; - unsigned long flags; + struct dz_serial * info = (struct dz_serial *)tty->driver_data; + unsigned long flags; - if (!info) return; - - save_flags (flags); - cli(); - - if (tty_hung_up_p (filp)) { - restore_flags (flags); - return; - } - - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("dz_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } - - if (--info->count < 0) { - printk("ds_close: bad serial port count for ttys%d: %d\n", - info->line, info->count); - info->count = 0; - } - - if (info->count) { - restore_flags (flags); - return; - } - info->flags |= DZ_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & DZ_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - if (info->flags & DZ_CALLOUT_ACTIVE) - info->callout_termios = *tty->termios; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - - if (info->closing_wait != DZ_CLOSING_WAIT_NONE) - tty_wait_until_sent (tty, info->closing_wait); - - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts. - */ - - shutdown (info); - - if (tty->driver.flush_buffer) - tty->driver.flush_buffer (tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer (tty); - tty->closing = 0; - info->event = 0; - info->tty = 0; - - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - (tty->ldisc.open)(tty); - } - if (info->blocked_open) { - if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(info->close_delay); - } - wake_up_interruptible (&info->open_wait); - } - - info->flags &= ~(DZ_NORMAL_ACTIVE | DZ_CALLOUT_ACTIVE | DZ_CLOSING); - wake_up_interruptible (&info->close_wait); - - restore_flags (flags); + if (!info) + return; + + save_and_cli(flags); + + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty structure + * will be freed. Info->count should always be one in these + * conditions. If it's greater than one, we've got real + * problems, since it means the serial port won't be shutdown. + */ + printk("dz_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + + if (--info->count < 0) { + printk("ds_close: bad serial port count for ttyS%02d: %d\n", + info->line, info->count); + info->count = 0; + } + + if (info->count) { + restore_flags(flags); + return; + } + info->flags |= DZ_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & DZ_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & DZ_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify the line + * discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + + if (info->closing_wait != DZ_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + + /* + * At this point we stop accepting input. To do this, we disable the + * receive line status interrupts. + */ + shutdown(info); + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer (tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer (tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + + if (tty->ldisc.num != ldiscs[N_TTY].num) { + if (tty->ldisc.close) + tty->ldisc.close(tty); + tty->ldisc = ldiscs[N_TTY]; + tty->termios->c_line = N_TTY; + if (tty->ldisc.open) + tty->ldisc.open(tty); + } + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + + info->flags &= ~(DZ_NORMAL_ACTIVE | DZ_CALLOUT_ACTIVE | DZ_CLOSING); + wake_up_interruptible(&info->close_wait); + + restore_flags(flags); } /* @@ -1105,15 +1152,15 @@ static void dz_close (struct tty_struct *tty, struct file *filp) */ static void dz_hangup (struct tty_struct *tty) { - struct dz_serial *info = (struct dz_serial *)tty->driver_data; + struct dz_serial *info = (struct dz_serial *) tty->driver_data; - dz_flush_buffer (tty); - shutdown (info); - info->event = 0; - info->count = 0; - info->flags &= ~(DZ_NORMAL_ACTIVE | DZ_CALLOUT_ACTIVE); - info->tty = 0; - wake_up_interruptible (&info->open_wait); + dz_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~(DZ_NORMAL_ACTIVE | DZ_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); } /* @@ -1121,101 +1168,103 @@ static void dz_hangup (struct tty_struct *tty) * rs_open() and friends * ------------------------------------------------------------ */ -static int block_til_ready (struct tty_struct *tty, struct file *filp, struct dz_serial *info) +static int block_til_ready(struct tty_struct *tty, struct file *filp, + struct dz_serial *info) { - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & DZ_CLOSING) { - interruptible_sleep_on (&info->close_wait); - return -EAGAIN; - } - - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { - if (info->flags & DZ_NORMAL_ACTIVE) - return -EBUSY; + DECLARE_WAITQUEUE(wait, current); + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & DZ_CLOSING) { + interruptible_sleep_on(&info->close_wait); + return -EAGAIN; + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & DZ_NORMAL_ACTIVE) + return -EBUSY; - if ((info->flags & DZ_CALLOUT_ACTIVE) && - (info->flags & DZ_SESSION_LOCKOUT) && - (info->session != current->session)) - return -EBUSY; + if ((info->flags & DZ_CALLOUT_ACTIVE) && + (info->flags & DZ_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; - if ((info->flags & DZ_CALLOUT_ACTIVE) && - (info->flags & DZ_PGRP_LOCKOUT) && - (info->pgrp != current->pgrp)) - return -EBUSY; - info->flags |= DZ_CALLOUT_ACTIVE; - return 0; - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - if (info->flags & DZ_CALLOUT_ACTIVE) - return -EBUSY; - info->flags |= DZ_NORMAL_ACTIVE; - return 0; - } - - if (info->flags & DZ_CALLOUT_ACTIVE) { - if (info->normal_termios.c_cflag & CLOCAL) - do_clocal = 1; - } else { - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - } - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * dz_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue (&info->open_wait, &wait); - - info->count--; - info->blocked_open++; - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p (filp) || !(info->is_initialized)) { - retval = -EAGAIN; - break; - } - if (!(info->flags & DZ_CALLOUT_ACTIVE) && - !(info->flags & DZ_CLOSING) && do_clocal) - break; - if (signal_pending (current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } + if ((info->flags & DZ_CALLOUT_ACTIVE) && + (info->flags & DZ_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + + info->flags |= DZ_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, then make + * the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & DZ_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= DZ_NORMAL_ACTIVE; + + return 0; + } + + if (info->flags & DZ_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become free + * (i.e., not in use by the callout). While we are in this loop, + * info->count is dropped by one, so that dz_close() knows when to free + * things. We restore it upon exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); + + info->count--; + info->blocked_open++; + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p (filp) || !(info->is_initialized)) { + retval = -EAGAIN; + break; + } + if (!(info->flags & DZ_CALLOUT_ACTIVE) && + !(info->flags & DZ_CLOSING) && do_clocal) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } - current->state = TASK_RUNNING; - remove_wait_queue (&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; - - if (retval) - return retval; - info->flags |= DZ_NORMAL_ACTIVE; - return 0; -} + current->state = TASK_RUNNING; + remove_wait_queue (&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; + + if (retval) + return retval; + info->flags |= DZ_NORMAL_ACTIVE; + return 0; +} /* * This routine is called whenever a serial port is opened. It @@ -1224,201 +1273,220 @@ static int block_til_ready (struct tty_struct *tty, struct file *filp, struct dz */ static int dz_open (struct tty_struct *tty, struct file *filp) { - struct dz_serial *info; - int retval, line; - - line = MINOR(tty->device) - tty->driver.minor_start; - - /* The dz lines for the mouse/keyboard must be - * opened using their respective drivers. - */ - if ((line < 0) || (line >= DZ_NB_PORT)) - return -ENODEV; + struct dz_serial *info; + int retval, line; - if ((line == DZ_KEYBOARD) || (line == DZ_MOUSE)) - return -ENODEV; + line = MINOR(tty->device) - tty->driver.minor_start; - info = lines[line]; - info->count++; - - tty->driver_data = info; - info->tty = tty; - - /* - * Start up serial port - */ - retval = startup (info); - if (retval) - return retval; + /* + * The dz lines for the mouse/keyboard must be opened using their + * respective drivers. + */ + if ((line < 0) || (line >= DZ_NB_PORT)) + return -ENODEV; + if ((line == DZ_KEYBOARD) || (line == DZ_MOUSE)) + return -ENODEV; + info = lines[line]; + info->count++; - retval = block_til_ready (tty, filp, info); - if (retval) - return retval; + tty->driver_data = info; + info->tty = tty; - if ((info->count == 1) && (info->flags & DZ_SPLIT_TERMIOS)) { - if (tty->driver.subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->normal_termios; - else - *tty->termios = info->callout_termios; - change_speed (info); + /* + * Start up serial port + */ + retval = startup (info); + if (retval) + return retval; + + retval = block_til_ready (tty, filp, info); + if (retval) + return retval; + + if ((info->count == 1) && (info->flags & DZ_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + change_speed(info); + } - } + info->session = current->session; + info->pgrp = current->pgrp; - info->session = current->session; - info->pgrp = current->pgrp; - return 0; + return 0; } static void show_serial_version (void) { - printk("%s%s\n", dz_name, dz_version); + printk("%s%s\n", dz_name, dz_version); } int __init dz_init(void) { - int i; - unsigned long flags; - struct dz_serial *info; - - /* Setup base handler, and timer table. */ - init_bh (SERIAL_BH, do_serial_bh); - - show_serial_version (); - - memset(&serial_driver, 0, sizeof(struct tty_driver)); - serial_driver.magic = TTY_DRIVER_MAGIC; - serial_driver.name = "ttyS"; - serial_driver.major = TTY_MAJOR; - serial_driver.minor_start = 64; - serial_driver.num = DZ_NB_PORT; - serial_driver.type = TTY_DRIVER_TYPE_SERIAL; - serial_driver.subtype = SERIAL_TYPE_NORMAL; - serial_driver.init_termios = tty_std_termios; - - serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver.flags = TTY_DRIVER_REAL_RAW; - serial_driver.refcount = &serial_refcount; - serial_driver.table = serial_table; - serial_driver.termios = serial_termios; - serial_driver.termios_locked = serial_termios_locked; - - serial_driver.open = dz_open; - serial_driver.close = dz_close; - serial_driver.write = dz_write; - serial_driver.flush_chars = dz_flush_chars; - serial_driver.write_room = dz_write_room; - serial_driver.chars_in_buffer = dz_chars_in_buffer; - serial_driver.flush_buffer = dz_flush_buffer; - serial_driver.ioctl = dz_ioctl; - serial_driver.throttle = dz_throttle; - serial_driver.unthrottle = dz_unthrottle; - serial_driver.send_xchar = dz_send_xchar; - serial_driver.set_termios = dz_set_termios; - serial_driver.stop = dz_stop; - serial_driver.start = dz_start; - serial_driver.hangup = dz_hangup; - - /* - * The callout device is just like normal device except for - * major number and the subtype code. - */ - callout_driver = serial_driver; - callout_driver.name = "cua"; - callout_driver.major = TTYAUX_MAJOR; - callout_driver.subtype = SERIAL_TYPE_CALLOUT; - - if (tty_register_driver (&serial_driver)) - panic("Couldn't register serial driver\n"); - if (tty_register_driver (&callout_driver)) - panic("Couldn't register callout driver\n"); - save_flags(flags); cli(); - - for (i=0; i < DZ_NB_PORT; i++) - { - info = &multi[i]; - lines[i] = info; - info->magic = SERIAL_MAGIC; - - if ((mips_machtype == MACH_DS23100) || (mips_machtype == MACH_DS5100)) - info->port = (unsigned long) KN01_DZ11_BASE; - else - info->port = (unsigned long) KN02_DZ11_BASE; - - info->line = i; - info->tty = 0; - info->close_delay = 50; - info->closing_wait = 3000; - info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - info->tqueue_hangup.routine = do_serial_hangup; - info->tqueue_hangup.data = info; - info->callout_termios = callout_driver.init_termios; - info->normal_termios = serial_driver.init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - - /* If we are pointing to address zero then punt - not correctly - set up in setup.c to handle this. */ - if (! info->port) - return 0; - - printk("ttyS%02d at 0x%04x (irq = %d)\n", info->line, info->port, SERIAL); - } - - /* reset the chip */ + int i, flags; + struct dz_serial *info; + + /* Setup base handler, and timer table. */ + init_bh(SERIAL_BH, do_serial_bh); + + show_serial_version(); + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + serial_driver.name = "ttyS"; +#else + serial_driver.name = "tts/%d"; +#endif + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = DZ_NB_PORT; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + + serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | + CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = dz_open; + serial_driver.close = dz_close; + serial_driver.write = dz_write; + serial_driver.flush_chars = dz_flush_chars; + serial_driver.write_room = dz_write_room; + serial_driver.chars_in_buffer = dz_chars_in_buffer; + serial_driver.flush_buffer = dz_flush_buffer; + serial_driver.ioctl = dz_ioctl; + serial_driver.throttle = dz_throttle; + serial_driver.unthrottle = dz_unthrottle; + serial_driver.send_xchar = dz_send_xchar; + serial_driver.set_termios = dz_set_termios; + serial_driver.stop = dz_stop; + serial_driver.start = dz_start; + serial_driver.hangup = dz_hangup; + + /* + * The callout device is just like normal device except for major + * number and the subtype code. + */ + callout_driver = serial_driver; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + callout_driver.name = "cua"; +#else + callout_driver.name = "cua/%d"; +#endif + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if (tty_register_driver (&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver (&callout_driver)) + panic("Couldn't register callout driver\n"); + + save_flags(flags); cli(); + for (i=0; i < DZ_NB_PORT; i++) { + info = &multi[i]; + lines[i] = info; + info->magic = SERIAL_MAGIC; + + if ((mips_machtype == MACH_DS23100) || + (mips_machtype == MACH_DS5100)) + info->port = (unsigned long) KN01_DZ11_BASE; + else + info->port = (unsigned long) KN02_DZ11_BASE; + + info->line = i; + info->tty = 0; + info->close_delay = 50; + info->closing_wait = 3000; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->tqueue_hangup.routine = do_serial_hangup; + info->tqueue_hangup.data = info; + info->callout_termios = callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + + /* + * If we are pointing to address zero then punt - not correctly + * set up in setup.c to handle this. + */ + if (! info->port) + return 0; + + printk("ttyS%02d at 0x%08x (irq = %d)\n", info->line, + info->port, SERIAL); + + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + info->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + info->line); + } + + /* Reset the chip */ #ifndef CONFIG_SERIAL_CONSOLE - dz_out(info, DZ_CSR, DZ_CLR); - while ((tmp = dz_in(info,DZ_CSR)) & DZ_CLR) ; - wbflush(); + { + int tmp; + dz_out(info, DZ_CSR, DZ_CLR); + while ((tmp = dz_in(info,DZ_CSR)) & DZ_CLR); + wbflush(); - /* enable scanning */ - dz_out(info, DZ_CSR, DZ_MSE); + /* Enable scanning */ + dz_out(info, DZ_CSR, DZ_MSE); + } #endif - /* order matters here... the trick is that flags - is updated... in request_irq - to immediatedly obliterate - it is unwise. */ - restore_flags(flags); - + /* + * Order matters here... the trick is that flags is updated... in + * request_irq - to immediatedly obliterate it is unwise. + */ + restore_flags(flags); - if (request_irq (SERIAL, dz_interrupt, SA_INTERRUPT, "DZ", lines[0])) - panic ("Unable to register DZ interrupt\n"); + if (request_irq(SERIAL, dz_interrupt, SA_INTERRUPT, "DZ", lines[0])) + panic("Unable to register DZ interrupt\n"); - return 0; + return 0; } #ifdef CONFIG_SERIAL_CONSOLE static void dz_console_put_char (unsigned char ch) { - unsigned long flags; - int loops = 2500; - unsigned short tmp = ch; - /* this code sends stuff out to serial device - spinning its - wheels and waiting. */ + unsigned long flags; + int loops = 2500; + unsigned short tmp = ch; + /* + * this code sends stuff out to serial device - spinning its wheels and + * waiting. + */ - /* force the issue - point it at lines[3]*/ - dz_console=&multi[CONSOLE_LINE]; + /* force the issue - point it at lines[3]*/ + dz_console = &multi[CONSOLE_LINE]; - save_flags(flags); - cli(); - + save_and_cli(flags); - /* spin our wheels */ - while (((dz_in(dz_console,DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--) - ; + /* spin our wheels */ + while (((dz_in(dz_console, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--) + ; - /* Actually transmit the character. */ - dz_out (dz_console, DZ_TDR, tmp); + /* Actually transmit the character. */ + dz_out(dz_console, DZ_TDR, tmp); - restore_flags(flags); + restore_flags(flags); } + /* * ------------------------------------------------------------------- * dz_console_print () @@ -1432,19 +1500,18 @@ static void dz_console_print (struct console *cons, unsigned int count) { #ifdef DEBUG_DZ - prom_printf((char *)str); + prom_printf((char *)str); #endif - while (count--) - { - if (*str == '\n') - dz_console_put_char ('\r'); - dz_console_put_char (*str++); - } + while (count--) { + if (*str == '\n') + dz_console_put_char('\r'); + dz_console_put_char(*str++); + } } static int dz_console_wait_key(struct console *co) { - return 0; + return 0; } static kdev_t dz_console_device(struct console *c) @@ -1454,17 +1521,17 @@ static kdev_t dz_console_device(struct console *c) static int __init dz_console_setup(struct console *co, char *options) { - int baud = 9600; - int bits = 8; - int parity = 'n'; - int cflag = CREAD | HUPCL | CLOCAL; - char *s; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + char *s; unsigned short mask,tmp; if (options) { baud = simple_strtoul(options, NULL, 10); s = options; - while(*s >= '0' && *s <= '9') + while (*s >= '0' && *s <= '9') s++; if (*s) parity = *s++; @@ -1473,9 +1540,9 @@ static int __init dz_console_setup(struct console *co, char *options) } /* - * Now construct a cflag setting. + * Now construct a cflag setting. */ - switch(baud) { + switch (baud) { case 1200: cflag |= DZ_B1200; break; @@ -1490,7 +1557,7 @@ static int __init dz_console_setup(struct console *co, char *options) cflag |= DZ_B9600; break; } - switch(bits) { + switch (bits) { case 7: cflag |= DZ_CS7; break; @@ -1499,11 +1566,13 @@ static int __init dz_console_setup(struct console *co, char *options) cflag |= DZ_CS8; break; } - switch(parity) { - case 'o': case 'O': + switch (parity) { + case 'o': + case 'O': cflag |= DZ_PARODD; break; - case 'e': case 'E': + case 'e': + case 'E': cflag |= DZ_PARENB; break; } @@ -1511,45 +1580,44 @@ static int __init dz_console_setup(struct console *co, char *options) /* TOFIX: force to console line */ dz_console = &multi[CONSOLE_LINE]; - if ((mips_machtype == MACH_DS23100) || (mips_machtype == MACH_DS5100)) - dz_console->port = KN01_DZ11_BASE; - else - dz_console->port = KN02_DZ11_BASE; + if ((mips_machtype == MACH_DS23100) || (mips_machtype == MACH_DS5100)) + dz_console->port = KN01_DZ11_BASE; + else + dz_console->port = KN02_DZ11_BASE; dz_console->line = CONSOLE_LINE; dz_out(dz_console, DZ_CSR, DZ_CLR); while ((tmp = dz_in(dz_console,DZ_CSR)) & DZ_CLR) - ; + ; /* enable scanning */ dz_out(dz_console, DZ_CSR, DZ_MSE); - /* Set up flags... */ + /* Set up flags... */ dz_console->cflags = 0; dz_console->cflags |= DZ_B9600; dz_console->cflags |= DZ_CS8; dz_console->cflags |= DZ_PARENB; - dz_out (dz_console, DZ_LPR, dz_console->cflags); + dz_out(dz_console, DZ_LPR, dz_console->cflags); mask = 1 << dz_console->line; - tmp = dz_in (dz_console, DZ_TCR); /* read the TX flag */ + tmp = dz_in (dz_console, DZ_TCR); /* read the TX flag */ if (!(tmp & mask)) { - tmp |= mask; /* set the TX flag */ - dz_out (dz_console, DZ_TCR, tmp); + tmp |= mask; /* set the TX flag */ + dz_out (dz_console, DZ_TCR, tmp); } - return 0; } static struct console dz_sercons = { - name: "ttyS", - write: dz_console_print, - device: dz_console_device, - wait_key: dz_console_wait_key, - setup: dz_console_setup, - flags: CON_CONSDEV | CON_PRINTBUFFER, - index: CONSOLE_LINE, + name: "ttyS", + write: dz_console_print, + device: dz_console_device, + wait_key: dz_console_wait_key, + setup: dz_console_setup, + flags: CON_CONSDEV | CON_PRINTBUFFER, + index: CONSOLE_LINE, }; void __init dz_serial_console_init(void) @@ -1559,3 +1627,4 @@ void __init dz_serial_console_init(void) #endif /* ifdef CONFIG_SERIAL_CONSOLE */ +MODULE_LICENSE("GPL"); diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 2a4433adda4a..3d7d692a52ec 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -610,9 +610,7 @@ static void pc_close(struct tty_struct * tty, struct file * filp) ASYNC_CALLOUT_ACTIVE | ASYNC_CLOSING); wake_up_interruptible(&ch->close_wait); -#ifdef MODULE MOD_DEC_USE_COUNT; -#endif restore_flags(flags); @@ -700,10 +698,8 @@ static void pc_hangup(struct tty_struct *tty) shutdown(ch); -#ifdef MODULE if (ch->count) MOD_DEC_USE_COUNT; -#endif /* MODULE */ ch->tty = NULL; @@ -1397,12 +1393,9 @@ static int pc_open(struct tty_struct *tty, struct file * filp) return(-ENODEV); } -#ifdef MODULE MOD_INC_USE_COUNT; -#endif - ch = &digi_channels[line]; boardnum = ch->boardnum; @@ -4106,3 +4099,5 @@ int __init init_PCI (void) } /* End init_PCI */ #endif /* ENABLE_PCI */ + +MODULE_LICENSE("GPL"); diff --git a/drivers/char/ite_gpio.c b/drivers/char/ite_gpio.c new file mode 100644 index 000000000000..1029ba80ed87 --- /dev/null +++ b/drivers/char/ite_gpio.c @@ -0,0 +1,430 @@ +/* + * FILE NAME ite_gpio.c + * + * BRIEF MODULE DESCRIPTION + * API for ITE GPIO device. + * Driver for ITE GPIO device. + * + * Author: MontaVista Software, Inc. <source@mvista.com> + * Hai-Pao Fan <haipao@mvista.com> + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <asm/uaccess.h> +#include <asm/addrspace.h> +#include <asm/it8172/it8172_int.h> +#include <linux/sched.h> +#include <linux/ite_gpio.h> + +#define ite_gpio_base 0x14013800 + +#define ITE_GPADR (*(volatile __u8 *)(0x14013800 + KSEG1)) +#define ITE_GPBDR (*(volatile __u8 *)(0x14013808 + KSEG1)) +#define ITE_GPCDR (*(volatile __u8 *)(0x14013810 + KSEG1)) +#define ITE_GPACR (*(volatile __u16 *)(0x14013802 + KSEG1)) +#define ITE_GPBCR (*(volatile __u16 *)(0x1401380a + KSEG1)) +#define ITE_GPCCR (*(volatile __u16 *)(0x14013812 + KSEG1)) +#define ITE_GPAICR (*(volatile __u16 *)(0x14013804 + KSEG1)) +#define ITE_GPBICR (*(volatile __u16 *)(0x1401380c + KSEG1)) +#define ITE_GPCICR (*(volatile __u16 *)(0x14013814 + KSEG1)) +#define ITE_GPAISR (*(volatile __u8 *)(0x14013806 + KSEG1)) +#define ITE_GPBISR (*(volatile __u8 *)(0x1401380e + KSEG1)) +#define ITE_GPCISR (*(volatile __u8 *)(0x14013816 + KSEG1)) +#define ITE_GCR (*(volatile __u8 *)(0x14013818 + KSEG1)) + +#define MAX_GPIO_LINE 21 +static int ite_gpio_irq=IT8172_GPIO_IRQ; + +static long ite_irq_counter[MAX_GPIO_LINE]; +wait_queue_head_t ite_gpio_wait[MAX_GPIO_LINE]; +static int ite_gpio_irq_pending[MAX_GPIO_LINE]; + +static int ite_gpio_debug=0; +#define DEB(x) if (ite_gpio_debug>=1) x + +int ite_gpio_in(__u32 device, __u32 mask, volatile __u32 *data) +{ + DEB(printk("ite_gpio_in mask=0x%x\n",mask)); + + switch (device) { + case ITE_GPIO_PORTA: + ITE_GPACR = (__u16)mask; /* 0xffff */ + *data = ITE_GPADR; + break; + case ITE_GPIO_PORTB: + ITE_GPBCR = (__u16)mask; /* 0xffff */ + *data = ITE_GPBDR; + break; + case ITE_GPIO_PORTC: + ITE_GPCCR = (__u16)mask; /* 0x03ff */ + *data = ITE_GPCDR; + break; + default: + return -EFAULT; + } + + return 0; +} + + +int ite_gpio_out(__u32 device, __u32 mask, __u32 data) +{ + switch (device) { + case ITE_GPIO_PORTA: + ITE_GPACR = (__u16)mask; /* 0x5555 */ + ITE_GPADR = (__u8)data; + break; + case ITE_GPIO_PORTB: + ITE_GPBCR = (__u16)mask; /* 0x5555 */ + ITE_GPBDR = (__u8)data; + break; + case ITE_GPIO_PORTC: + ITE_GPCCR = (__u16)mask; /* 0x0155 */ + ITE_GPCDR = (__u8)data; + break; + default: + return -EFAULT; + } + + return 0; +} + +int ite_gpio_int_ctrl(__u32 device, __u32 mask, __u32 data) +{ + switch (device) { + case ITE_GPIO_PORTA: + ITE_GPAICR = (ITE_GPAICR & ~mask) | (data & mask); + break; + case ITE_GPIO_PORTB: + ITE_GPBICR = (ITE_GPBICR & ~mask) | (data & mask); + break; + case ITE_GPIO_PORTC: + ITE_GPCICR = (ITE_GPCICR & ~mask) | (data & mask); + break; + default: + return -EFAULT; + } + + return 0; +} + +int ite_gpio_in_status(__u32 device, __u32 mask, volatile __u32 *data) +{ + int ret=-1; + + if (MAX_GPIO_LINE > *data >= 0) + ret=ite_gpio_irq_pending[*data]; + + DEB(printk("ite_gpio_in_status %d ret=%d\n",*data, ret)); + + switch (device) { + case ITE_GPIO_PORTA: + *data = ITE_GPAISR & mask; + break; + case ITE_GPIO_PORTB: + *data = ITE_GPBISR & mask; + break; + case ITE_GPIO_PORTC: + *data = ITE_GPCISR & mask; + break; + default: + return -EFAULT; + } + + return ret; +} + +int ite_gpio_out_status(__u32 device, __u32 mask, __u32 data) +{ + switch (device) { + case ITE_GPIO_PORTA: + ITE_GPAISR = (ITE_GPAISR & ~mask) | (data & mask); + break; + case ITE_GPIO_PORTB: + ITE_GPBISR = (ITE_GPBISR & ~mask) | (data & mask); + break; + case ITE_GPIO_PORTC: + ITE_GPCISR = (ITE_GPCISR & ~mask) | (data & mask); + break; + default: + return -EFAULT; + } + + return 0; +} + +int ite_gpio_gen_ctrl(__u32 device, __u32 mask, __u32 data) +{ + ITE_GCR = (ITE_GCR & ~mask) | (data & mask); + + return 0; +} + +int ite_gpio_int_wait (__u32 device, __u32 mask, __u32 data) +{ + int i,line=0, ret=0; + unsigned long flags; + + switch (device) { + case ITE_GPIO_PORTA: + line = data & mask; + break; + case ITE_GPIO_PORTB: + line = (data & mask) <<8; + break; + case ITE_GPIO_PORTC: + line = (data & mask) <<16; + break; + } + for (i=MAX_GPIO_LINE-1; i >= 0; i--) { + if ( (line) & (1 << i)) + break; + } + + DEB(printk("wait device=0x%d mask=0x%x data=0x%x index %d\n", + device, mask, data, i)); + + if (line & ~(1<<i)) + return -EFAULT; + + if (ite_gpio_irq_pending[i]==1) + return -EFAULT; + + save_flags (flags); + cli(); + ite_gpio_irq_pending[i] = 1; + ret = interruptible_sleep_on_timeout(&ite_gpio_wait[i], 3*HZ); + restore_flags (flags); + ite_gpio_irq_pending[i] = 0; + + return ret; +} + +EXPORT_SYMBOL(ite_gpio_in); +EXPORT_SYMBOL(ite_gpio_out); +EXPORT_SYMBOL(ite_gpio_int_ctrl); +EXPORT_SYMBOL(ite_gpio_in_status); +EXPORT_SYMBOL(ite_gpio_out_status); +EXPORT_SYMBOL(ite_gpio_gen_ctrl); +EXPORT_SYMBOL(ite_gpio_int_wait); + +static int ite_gpio_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + if (minor != GPIO_MINOR) + return -ENODEV; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + return 0; +} + + +static int ite_gpio_release(struct inode *inode, struct file *file) +{ + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + + +static int ite_gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + + static struct ite_gpio_ioctl_data ioctl_data; + + if (copy_from_user(&ioctl_data, (struct ite_gpio_ioctl_data *)arg, + sizeof(ioctl_data))) + return -EFAULT; + if ((ioctl_data.device < ITE_GPIO_PORTA) || + (ioctl_data.device > ITE_GPIO_PORTC) ) + return -EFAULT; + + switch(cmd) { + case ITE_GPIO_IN: + if (ite_gpio_in(ioctl_data.device, ioctl_data.mask, + &ioctl_data.data)) + return -EFAULT; + + if (copy_to_user((struct ite_gpio_ioctl_data *)arg, + &ioctl_data, sizeof(ioctl_data))) + return -EFAULT; + break; + + case ITE_GPIO_OUT: + return ite_gpio_out(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + case ITE_GPIO_INT_CTRL: + return ite_gpio_int_ctrl(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + case ITE_GPIO_IN_STATUS: + if (ite_gpio_in_status(ioctl_data.device, ioctl_data.mask, + &ioctl_data.data)) + return -EFAULT; + if (copy_to_user((struct ite_gpio_ioctl_data *)arg, + &ioctl_data, sizeof(ioctl_data))) + return -EFAULT; + break; + + case ITE_GPIO_OUT_STATUS: + return ite_gpio_out_status(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + case ITE_GPIO_GEN_CTRL: + return ite_gpio_gen_ctrl(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + case ITE_GPIO_INT_WAIT: + return ite_gpio_int_wait(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + default: + return -ENOIOCTLCMD; + + } + return 0; +} + +static void ite_gpio_irq_handler(int this_irq, void *dev_id, struct pt_regs *regs) +{ + int i,line; + + line = ITE_GPCISR & 0x1f; + for (i=4; i >=0; i--) { + if ( line & (1 << i)) { + ++ite_irq_counter[i+16]; + ite_gpio_irq_pending[i+16] = 2; + wake_up_interruptible(&ite_gpio_wait[i+16]); + +DEB(printk("interrupt 0x%x %d\n", &ite_gpio_wait[i+16], i+16)); + + ITE_GPCISR = ITE_GPCISR & (1<<i); + return; + } + } + line = ITE_GPBISR; + for (i=7; i >= 0; i--) { + if ( line & (1 << i)) { + ++ite_irq_counter[i+8]; + ite_gpio_irq_pending[i+8] = 2; + wake_up_interruptible(&ite_gpio_wait[i+8]); + +DEB(printk("interrupt 0x%x %d\n",ITE_GPBISR, i+8)); + + ITE_GPBISR = ITE_GPBISR & (1<<i); + return; + } + } + line = ITE_GPAISR; + for (i=7; i >= 0; i--) { + if ( line & (1 << i)) { + ++ite_irq_counter[i]; + ite_gpio_irq_pending[i] = 2; + wake_up_interruptible(&ite_gpio_wait[i]); + +DEB(printk("interrupt 0x%x %d\n",ITE_GPAISR, i)); + + ITE_GPAISR = ITE_GPAISR & (1<<i); + return; + } + } +} + +static struct file_operations ite_gpio_fops = +{ + owner: THIS_MODULE, + ioctl: ite_gpio_ioctl, + open: ite_gpio_open, + release: ite_gpio_release, +}; + +/* GPIO_MINOR in include/linux/miscdevice.h */ +static struct miscdevice ite_gpio_miscdev = +{ + GPIO_MINOR, + "ite_gpio", + &ite_gpio_fops +}; + +int __init ite_gpio_init(void) +{ + int i; + + misc_register(&ite_gpio_miscdev); + + if (check_region(ite_gpio_base, 0x1c) < 0 ) { + return -ENODEV; + } else { + request_region(ite_gpio_base, 0x1c, "ITE GPIO"); + } + + /* initialize registers */ + ITE_GPACR = 0xffff; + ITE_GPBCR = 0xffff; + ITE_GPCCR = 0xffff; + ITE_GPAICR = 0x00ff; + ITE_GPBICR = 0x00ff; + ITE_GPCICR = 0x00ff; + ITE_GCR = 0; + + for (i = 0; i < MAX_GPIO_LINE; i++) { + ite_gpio_irq_pending[i]=0; + init_waitqueue_head(&ite_gpio_wait[i]); + } + if (request_irq(ite_gpio_irq, ite_gpio_irq_handler, SA_SHIRQ, "gpio", 0) < 0) + return 0; + printk("GPIO at 0x%x (irq = %d)\n", ite_gpio_base, ite_gpio_irq); + + return 0; +} + +void __exit ite_gpio_exit(void) +{ + misc_deregister(&ite_gpio_miscdev); +} + +module_init(ite_gpio_init); +module_exit(ite_gpio_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6b0c3ee867c3..2f340ef36bcb 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -153,8 +153,6 @@ static inline pgprot_t pgprot_noncached(pgprot_t _prot) /* Use no-cache mode, serialized */ else if (MMU_IS_040 || MMU_IS_060) prot = (prot & _CACHEMASK040) | _PAGE_NOCACHE_S; -#elif defined(__mips__) - prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED; #endif return __pgprot(prot); diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 0f885ca302da..a9e22cb610e5 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -205,6 +205,7 @@ static int numports[] = {0, 0, 0, 0}; MODULE_AUTHOR("William Chen"); MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); +MODULE_LICENSE("GPL"); MODULE_PARM(type, "1-4i"); MODULE_PARM(baseaddr, "1-4i"); MODULE_PARM(numports, "1-4i"); @@ -212,6 +213,8 @@ MODULE_PARM(ttymajor, "i"); MODULE_PARM(calloutmajor, "i"); MODULE_PARM(verbose, "i"); +EXPORT_NO_SYMBOLS; + #endif //MODULE static struct tty_driver moxaDriver; @@ -1717,27 +1720,6 @@ int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port) if(copy_to_user((void *)arg, temp_queue, sizeof(struct moxaq_str) * MAX_PORTS)) return -EFAULT; return (0); - case MOXA_LOAD_BIOS: - if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) - return -EFAULT; - i = moxaloadbios(dltmp.cardno, dltmp.buf, dltmp.len); - return (i); - case MOXA_FIND_BOARD: - if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) - return -EFAULT; - return moxafindcard(dltmp.cardno); - case MOXA_LOAD_C320B: - if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) - return -EFAULT; - moxaload320b(dltmp.cardno, dltmp.buf, dltmp.len); - return (0); - case MOXA_LOAD_CODE: - if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) - return -EFAULT; - i = moxaloadcode(dltmp.cardno, dltmp.buf, dltmp.len); - if (i == -1) - return (-EFAULT); - return (i); case MOXA_GET_OQUEUE: i = MoxaPortTxQueue(port); return put_user(i, (unsigned long *) arg); @@ -1778,9 +1760,38 @@ int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port) if(copy_to_user((void *)arg, GMStatus, sizeof(struct mxser_mstatus) * MAX_PORTS)) return -EFAULT; return 0; + default: + return (-ENOIOCTLCMD); + case MOXA_LOAD_BIOS: + case MOXA_FIND_BOARD: + case MOXA_LOAD_C320B: + case MOXA_LOAD_CODE: + break; + } + + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + if(dltmp.cardno < 0 || dltmp.cardno >= MAX_BOARDS) + return -EINVAL; + + switch(cmd) + { + case MOXA_LOAD_BIOS: + i = moxaloadbios(dltmp.cardno, dltmp.buf, dltmp.len); + return (i); + case MOXA_FIND_BOARD: + return moxafindcard(dltmp.cardno); + case MOXA_LOAD_C320B: + moxaload320b(dltmp.cardno, dltmp.buf, dltmp.len); + default: /* to keep gcc happy */ + return (0); + case MOXA_LOAD_CODE: + i = moxaloadcode(dltmp.cardno, dltmp.buf, dltmp.len); + if (i == -1) + return (-EFAULT); + return (i); } - return (-ENOIOCTLCMD); } int MoxaDriverPoll(void) diff --git a/drivers/char/qtronix.c b/drivers/char/qtronix.c index c74aabf2b452..28e7668ce2df 100644 --- a/drivers/char/qtronix.c +++ b/drivers/char/qtronix.c @@ -6,7 +6,7 @@ * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. - * ppopov@mvista.com or support@mvista.com + * ppopov@mvista.com or source@mvista.com * * * The bottom portion of this driver was take from @@ -71,7 +71,7 @@ #include <linux/random.h> #include <linux/poll.h> #include <linux/miscdevice.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/kbd_kern.h> #include <linux/smp_lock.h> #include <asm/io.h> @@ -353,19 +353,19 @@ spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; static unsigned char handle_kbd_event(void); -int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) +int kbd_setkeycode(unsigned int scancode, unsigned int keycode) { - printk("pckbd_setkeycode scancode %x keycode %x\n", scancode, keycode); + printk("kbd_setkeycode scancode %x keycode %x\n", scancode, keycode); return 0; } -int pckbd_getkeycode(unsigned int scancode) +int kbd_getkeycode(unsigned int scancode) { return scancode; } -int pckbd_translate(unsigned char scancode, unsigned char *keycode, +int kbd_translate(unsigned char scancode, unsigned char *keycode, char raw_mode) { static int prev_scancode = 0; @@ -406,9 +406,9 @@ int pckbd_translate(unsigned char scancode, unsigned char *keycode, return 1; } -char pckbd_unexpected_up(unsigned char keycode) +char kbd_unexpected_up(unsigned char keycode) { - //printk("pckbd_unexpected_up\n"); + //printk("kbd_unexpected_up\n"); return 0; } @@ -422,12 +422,12 @@ static inline void handle_keyboard_event(unsigned char scancode, int down) } -void pckbd_leds(unsigned char leds) +void kbd_leds(unsigned char leds) { } /* dummy */ -void pckbd_init_hw(void) +void kbd_init_hw(void) { } diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 0c3a4c4bec4d..33edd15a7b8f 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -40,9 +40,10 @@ * 1.10b Andrew Morton: SMP lock fix * 1.10c Cesar Barros: SMP locking fixes and cleanup * 1.10d Paul Gortmaker: delete paranoia check in rtc_exit + * 1.10e Maciej W. Rozycki: Handle DECstation's year weirdness. */ -#define RTC_VERSION "1.10d" +#define RTC_VERSION "1.10e" #define RTC_IO_EXTENT 0x10 /* Only really two ports, but... */ @@ -361,6 +362,9 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned char mon, day, hrs, min, sec, leap_yr; unsigned char save_control, save_freq_select; unsigned int yrs; +#ifdef CONFIG_DECSTATION + unsigned int real_yrs; +#endif if (!capable(CAP_SYS_TIME)) return -EACCES; @@ -394,6 +398,20 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EINVAL; spin_lock_irq(&rtc_lock); +#ifdef CONFIG_DECSTATION + real_yrs = yrs; + yrs = 72; + + /* + * We want to keep the year set to 73 until March + * for non-leap years, so that Feb, 29th is handled + * correctly. + */ + if (!leap_yr && mon < 3) { + real_yrs--; + yrs = 73; + } +#endif if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { if (yrs > 169) { @@ -416,6 +434,9 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, save_freq_select = CMOS_READ(RTC_FREQ_SELECT); CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); +#ifdef CONFIG_DECSTATION + CMOS_WRITE(real_yrs, RTC_DEC_YEAR); +#endif CMOS_WRITE(yrs, RTC_YEAR); CMOS_WRITE(mon, RTC_MONTH); CMOS_WRITE(day, RTC_DAY_OF_MONTH); @@ -469,7 +490,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, spin_unlock_irq(&rtc_lock); return 0; } -#elif !defined(CONFIG_DECSTATION) +#endif case RTC_EPOCH_READ: /* Read the epoch. */ { return put_user (epoch, (unsigned long *)arg); @@ -488,7 +509,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, epoch = arg; return 0; } -#endif default: return -EINVAL; } @@ -709,12 +729,12 @@ found: } else if (year >= 20 && year < 48) { epoch = 1980; guess = "ARC console"; - } else if (year >= 48 && year < 70) { + } else if (year >= 48 && year < 72) { epoch = 1952; guess = "Digital UNIX"; #if defined(__mips__) - } else if (year >= 70 && year < 100) { - epoch = 1928; + } else if (year >= 72 && year < 74) { + epoch = 2000; guess = "Digital DECstation"; #else } else if (year >= 70) { @@ -911,6 +931,9 @@ static void get_rtc_time(struct rtc_time *rtc_tm) { unsigned long uip_watchdog = jiffies; unsigned char ctrl; +#ifdef CONFIG_DECSTATION + unsigned int real_year; +#endif /* * read RTC once any update in progress is done. The update @@ -939,6 +962,9 @@ static void get_rtc_time(struct rtc_time *rtc_tm) rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); rtc_tm->tm_year = CMOS_READ(RTC_YEAR); +#ifdef CONFIG_DECSTATION + real_year = CMOS_READ(RTC_DEC_YEAR); +#endif ctrl = CMOS_READ(RTC_CONTROL); spin_unlock_irq(&rtc_lock); @@ -952,6 +978,10 @@ static void get_rtc_time(struct rtc_time *rtc_tm) BCD_TO_BIN(rtc_tm->tm_year); } +#ifdef CONFIG_DECSTATION + rtc_tm->tm_year += real_year - 72; +#endif + /* * Account for differences between how the RTC uses the values * and how they are defined in a struct rtc_time; @@ -1024,3 +1054,6 @@ static void set_rtc_irq_bit(unsigned char bit) spin_unlock_irq(&rtc_lock); } #endif + +MODULE_AUTHOR("Paul Gortmaker"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/i2c-adap-ite.c b/drivers/i2c/i2c-adap-ite.c new file mode 100644 index 000000000000..c6a35dafcf32 --- /dev/null +++ b/drivers/i2c/i2c-adap-ite.c @@ -0,0 +1,319 @@ +/* + ------------------------------------------------------------------------- + i2c-adap-ite.c i2c-hw access for the IIC peripheral on the ITE MIPS system + ------------------------------------------------------------------------- + Hai-Pao Fan, MontaVista Software, Inc. + hpfan@mvista.com or source@mvista.com + + Copyright 2001 MontaVista Software Inc. + + ---------------------------------------------------------------------------- + This file was highly leveraged from i2c-elektor.c, which was created + by Simon G. Vogl and Hans Berglund: + + + Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even + Frodo Looijaard <frodol@dds.nl> */ + +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/version.h> +#include <linux/init.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-ite.h> +#include <linux/i2c-adap-ite.h> +#include "i2c-ite.h" + +#define DEFAULT_BASE 0x14014030 +#define ITE_IIC_IO_SIZE 0x40 +#define DEFAULT_IRQ 0 +#define DEFAULT_CLOCK 0x1b0e /* default 16MHz/(27+14) = 400KHz */ +#define DEFAULT_OWN 0x55 + +static int base = 0; +static int irq = 0; +static int clock = 0; +static int own = 0; + +static int i2c_debug=0; +static struct iic_ite gpi; +#if (LINUX_VERSION_CODE < 0x020301) +static struct wait_queue *iic_wait = NULL; +#else +static wait_queue_head_t iic_wait; +#endif +static int iic_pending; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x +#define DEBE(x) x /* error messages */ + + +/* ----- local functions ---------------------------------------------- */ + +static void iic_ite_setiic(void *data, int ctl, short val) +{ + unsigned long j = jiffies + 10; + + DEB3(printk(" Write 0x%02x to 0x%x\n",(unsigned short)val, ctl&0xff)); + DEB3({while (jiffies < j) schedule();}) + outw(val,ctl); +} + +static short iic_ite_getiic(void *data, int ctl) +{ + short val; + + val = inw(ctl); + DEB3(printk("Read 0x%02x from 0x%x\n",(unsigned short)val, ctl&0xff)); + return (val); +} + +/* Return our slave address. This is the address + * put on the I2C bus when another master on the bus wants to address us + * as a slave + */ +static int iic_ite_getown(void *data) +{ + return (gpi.iic_own); +} + + +static int iic_ite_getclock(void *data) +{ + return (gpi.iic_clock); +} + + +#if 0 +static void iic_ite_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} +#endif + + +/* Put this process to sleep. We will wake up when the + * IIC controller interrupts. + */ +static void iic_ite_waitforpin(void) { + + int timeout = 2; + + /* If interrupts are enabled (which they are), then put the process to + * sleep. This process will be awakened by two events -- either the + * the IIC peripheral interrupts or the timeout expires. + * If interrupts are not enabled then delay for a reasonable amount + * of time and return. + */ + if (gpi.iic_irq > 0) { + cli(); + if (iic_pending == 0) { + interruptible_sleep_on_timeout(&iic_wait, timeout*HZ ); + } else + iic_pending = 0; + sti(); + } else { + udelay(100); + } +} + + +static void iic_ite_handler(int this_irq, void *dev_id, struct pt_regs *regs) +{ + + iic_pending = 1; + + DEB2(printk("iic_ite_handler: in interrupt handler\n")); + wake_up_interruptible(&iic_wait); +} + + +/* Lock the region of memory where I/O registers exist. Request our + * interrupt line and register its associated handler. + */ +static int iic_hw_resrc_init(void) +{ + if (check_region(gpi.iic_base, ITE_IIC_IO_SIZE) < 0 ) { + return -ENODEV; + } else { + request_region(gpi.iic_base, ITE_IIC_IO_SIZE, + "i2c (i2c bus adapter)"); + } + if (gpi.iic_irq > 0) { + if (request_irq(gpi.iic_irq, iic_ite_handler, 0, "ITE IIC", 0) < 0) { + gpi.iic_irq = 0; + } else + DEB3(printk("Enabled IIC IRQ %d\n", gpi.iic_irq)); + enable_irq(gpi.iic_irq); + } + return 0; +} + + +static void iic_ite_release(void) +{ + if (gpi.iic_irq > 0) { + disable_irq(gpi.iic_irq); + free_irq(gpi.iic_irq, 0); + } + release_region(gpi.iic_base , 2); +} + + +static int iic_ite_reg(struct i2c_client *client) +{ + return 0; +} + + +static int iic_ite_unreg(struct i2c_client *client) +{ + return 0; +} + + +static void iic_ite_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + + +static void iic_ite_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_iic_data iic_ite_data = { + NULL, + iic_ite_setiic, + iic_ite_getiic, + iic_ite_getown, + iic_ite_getclock, + iic_ite_waitforpin, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter iic_ite_ops = { + "ITE IIC adapter", + I2C_HW_I_IIC, + NULL, + &iic_ite_data, + iic_ite_inc_use, + iic_ite_dec_use, + iic_ite_reg, + iic_ite_unreg, +}; + +/* Called when the module is loaded. This function starts the + * cascade of calls up through the heirarchy of i2c modules (i.e. up to the + * algorithm layer and into to the core layer) + */ +static int __init iic_ite_init(void) +{ + + struct iic_ite *piic = &gpi; + + printk(KERN_INFO "Initialize ITE IIC adapter module\n"); + if (base == 0) + piic->iic_base = DEFAULT_BASE; + else + piic->iic_base = base; + + if (irq == 0) + piic->iic_irq = DEFAULT_IRQ; + else + piic->iic_irq = irq; + + if (clock == 0) + piic->iic_clock = DEFAULT_CLOCK; + else + piic->iic_clock = clock; + + if (own == 0) + piic->iic_own = DEFAULT_OWN; + else + piic->iic_own = own; + + iic_ite_data.data = (void *)piic; +#if (LINUX_VERSION_CODE >= 0x020301) + init_waitqueue_head(&iic_wait); +#endif + if (iic_hw_resrc_init() == 0) { + if (i2c_iic_add_bus(&iic_ite_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + printk(KERN_INFO " found device at %#x irq %d.\n", + piic->iic_base, piic->iic_irq); + return 0; +} + + +static void iic_ite_exit(void) +{ + i2c_iic_del_bus(&iic_ite_ops); + iic_ite_release(); +} + +EXPORT_NO_SYMBOLS; + +/* If modules is NOT defined when this file is compiled, then the MODULE_* + * macros will resolve to nothing + */ +MODULE_AUTHOR("MontaVista Software <www.mvista.com>"); +MODULE_DESCRIPTION("I2C-Bus adapter routines for ITE IIC bus adapter"); + +MODULE_PARM(base, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(clock, "i"); +MODULE_PARM(own, "i"); +MODULE_PARM(i2c_debug,"i"); + + +/* Called when module is loaded or when kernel is intialized. + * If MODULES is defined when this file is compiled, then this function will + * resolve to init_module (the function called when insmod is invoked for a + * module). Otherwise, this function is called early in the boot, when the + * kernel is intialized. Check out /include/init.h to see how this works. + */ +module_init(iic_ite_init); + +/* Resolves to module_cleanup when MODULES is defined. */ +module_exit(iic_ite_exit); diff --git a/drivers/i2c/i2c-algo-ite.c b/drivers/i2c/i2c-algo-ite.c new file mode 100644 index 000000000000..194d1f78a732 --- /dev/null +++ b/drivers/i2c/i2c-algo-ite.c @@ -0,0 +1,872 @@ +/* + ------------------------------------------------------------------------- + i2c-algo-ite.c i2c driver algorithms for ITE adapters + + Hai-Pao Fan, MontaVista Software, Inc. + hpfan@mvista.com or source@mvista.com + + Copyright 2000 MontaVista Software Inc. + + --------------------------------------------------------------------------- + This file was highly leveraged from i2c-algo-pcf.c, which was created + by Simon G. Vogl and Hans Berglund: + + + Copyright (C) 1995-1997 Simon G. Vogl + 1998-2000 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and + Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey + <mbailey@littlefeet-inc.com> */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/version.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/sched.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-ite.h> +#include "i2c-ite.h" + +#define PM_DSR IT8172_PCI_IO_BASE + IT_PM_DSR +#define PM_IBSR IT8172_PCI_IO_BASE + IT_PM_DSR + 0x04 +#define GPIO_CCR IT8172_PCI_IO_BASE + IT_GPCCR + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) x; + /* debug the protocol by showing transferred bits */ +#define DEF_TIMEOUT 16 + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=1; +static int iic_test=0; /* see if the line-setting functions work */ +static int iic_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define get_clock(adap) adap->getclock(adap->data) +#define iic_outw(adap, reg, val) adap->setiic(adap->data, reg, val) +#define iic_inw(adap, reg) adap->getiic(adap->data, reg) + + +/* --- other auxiliary functions -------------------------------------- */ + +static void iic_start(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap,ITE_I2CHCR,ITE_CMD); +} + +static void iic_stop(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap,ITE_I2CHCR,0); + iic_outw(adap,ITE_I2CHSR,ITE_I2CHSR_TDI); +} + +static void iic_reset(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap, PM_IBSR, iic_inw(adap, PM_IBSR) | 0x80); +} + + +static int wait_for_bb(struct i2c_algo_iic_data *adap) +{ + int timeout = DEF_TIMEOUT; + short status; + + status = iic_inw(adap, ITE_I2CHSR); +#ifndef STUB_I2C + while (timeout-- && (status & ITE_I2CHSR_HB)) { + udelay(1000); /* How much is this? */ + status = iic_inw(adap, ITE_I2CHSR); + } +#endif + if (timeout<=0) { + printk(KERN_ERR "Timeout, host is busy\n"); + iic_reset(adap); + } + return(timeout<=0); +} + +/* + * Puts this process to sleep for a period equal to timeout + */ +static inline void iic_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} + +/* After we issue a transaction on the IIC bus, this function + * is called. It puts this process to sleep until we get an interrupt from + * from the controller telling us that the transaction we requested in complete. + */ +static int wait_for_pin(struct i2c_algo_iic_data *adap, short *status) { + + int timeout = DEF_TIMEOUT; + + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("Timeout waiting for host not busy\n");) + return -EIO; + } + timeout = DEF_TIMEOUT; + + *status = iic_inw(adap, ITE_I2CHSR); +#ifndef STUB_I2C + while (timeout-- && !(*status & ITE_I2CHSR_TDI)) { + adap->waitforpin(); + *status = iic_inw(adap, ITE_I2CHSR); + } +#endif + if (timeout <= 0) + return(-1); + else + return(0); +} + +static int wait_for_fe(struct i2c_algo_iic_data *adap, short *status) +{ + int timeout = DEF_TIMEOUT; + + *status = iic_inw(adap, ITE_I2CFSR); +#ifndef STUB_I2C + while (timeout-- && (*status & ITE_I2CFSR_FE)) { + udelay(1000); + iic_inw(adap, ITE_I2CFSR); + } +#endif + if (timeout <= 0) + return(-1); + else + return(0); +} + +static int iic_init (struct i2c_algo_iic_data *adap) +{ + short i; + + /* Clear bit 7 to set I2C to normal operation mode */ + i=iic_inw(adap, PM_DSR)& 0xff7f; + iic_outw(adap, PM_DSR, i); + + /* set IT_GPCCR port C bit 2&3 as function 2 */ + i = iic_inw(adap, GPIO_CCR) & 0xfc0f; + iic_outw(adap,GPIO_CCR,i); + + /* Clear slave address/sub-address */ + iic_outw(adap,ITE_I2CSAR, 0); + iic_outw(adap,ITE_I2CSSAR, 0); + + /* Set clock counter register */ + iic_outw(adap,ITE_I2CCKCNT, get_clock(adap)); + + /* Set START/reSTART/STOP time registers */ + iic_outw(adap,ITE_I2CSHDR, 0x0a); + iic_outw(adap,ITE_I2CRSUR, 0x0a); + iic_outw(adap,ITE_I2CPSUR, 0x0a); + + /* Enable interrupts on completing the current transaction */ + iic_outw(adap,ITE_I2CHCR, ITE_I2CHCR_IE | ITE_I2CHCR_HCE); + + /* Clear transfer count */ + iic_outw(adap,ITE_I2CFBCR, 0x0); + + DEB2(printk("iic_init: Initialized IIC on ITE 0x%x\n", + iic_inw(adap, ITE_I2CHSR))); + return 0; +} + + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_iic_data *adap, char *name) { +#if 0 + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("test_bus: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("test_bus: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("test_bus: %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("test_bus:1 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("test_bus: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("test_bus:2 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL unexpected low while SDA high!\n", + adap->name); + goto bailout; + } + scllo(adap); + printk("test_bus:3 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getscl(adap) ) { + + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("test_bus:4 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("test_bus: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +#endif + return (0); +} + +/* ----- Utility functions + */ + + +/* Verify the device we want to talk to on the IIC bus really exists. */ +static inline int try_address(struct i2c_algo_iic_data *adap, + unsigned int addr, int retries) +{ + int i, ret = -1; + short status; + + for (i=0;i<retries;i++) { + iic_outw(adap, ITE_I2CSAR, addr); + iic_start(adap); + if (wait_for_pin(adap, &status) == 0) { + if ((status & ITE_I2CHSR_DNE) == 0) { + iic_stop(adap); + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + ret=1; + break; /* success! */ + } + } + iic_stop(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("try_address: needed %d retries for 0x%x\n",i, + addr)); + return ret; +} + + +static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, + int count) +{ + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + int wrcount=0, timeout; + short status; + int loops, remainder, i, j; + union { + char byte[2]; + unsigned short word; + } tmp; + + iic_outw(adap, ITE_I2CSSAR, (unsigned short)buf[wrcount++]); + count--; + if (count == 0) + return -EIO; + + loops = count / 32; /* 32-byte FIFO */ + remainder = count % 32; + + if(loops) { + for(i=0; i<loops; i++) { + + iic_outw(adap, ITE_I2CFBCR, 32); + for(j=0; j<32/2; j++) { + tmp.byte[1] = buf[wrcount++]; + tmp.byte[0] = buf[wrcount++]; + iic_outw(adap, ITE_I2CFDR, tmp.word); + } + + /* status FIFO overrun */ + iic_inw(adap, ITE_I2CFSR); + iic_inw(adap, ITE_I2CFBCR); + + iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */ + + /* Wait for transmission to complete */ + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } + } + } + if(remainder) { + iic_outw(adap, ITE_I2CFBCR, remainder); + for(i=0; i<remainder/2; i++) { + tmp.byte[1] = buf[wrcount++]; + tmp.byte[0] = buf[wrcount++]; + iic_outw(adap, ITE_I2CFDR, tmp.word); + } + + /* status FIFO overrun */ + iic_inw(adap, ITE_I2CFSR); + iic_inw(adap, ITE_I2CFBCR); + + iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } +#endif + } + iic_stop(adap); + return wrcount; +} + + +static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count, + int sread) +{ + int rdcount=0, i, timeout; + short status; + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + int loops, remainder, j; + union { + char byte[2]; + unsigned short word; + } tmp; + + loops = count / 32; /* 32-byte FIFO */ + remainder = count % 32; + + if(loops) { + for(i=0; i<loops; i++) { + iic_outw(adap, ITE_I2CFBCR, 32); + if (sread) + iic_outw(adap, ITE_I2CHCR, ITE_SREAD); + else + iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s read timeout.\n", i2c_adap->name); + return (-1); + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name); + return (-1); + } +#endif + + timeout = wait_for_fe(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name); + return (-1); + } + + for(j=0; j<32/2; j++) { + tmp.word = iic_inw(adap, ITE_I2CFDR); + buf[rdcount++] = tmp.byte[1]; + buf[rdcount++] = tmp.byte[0]; + } + + /* status FIFO underrun */ + iic_inw(adap, ITE_I2CFSR); + + } + } + + + if(remainder) { + remainder=(remainder+1)/2 * 2; + iic_outw(adap, ITE_I2CFBCR, remainder); + if (sread) + iic_outw(adap, ITE_I2CHCR, ITE_SREAD); + else + iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s read timeout.\n", i2c_adap->name); + return (-1); + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name); + return (-1); + } +#endif + timeout = wait_for_fe(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name); + return (-1); + } + + for(i=0; i<(remainder+1)/2; i++) { + tmp.word = iic_inw(adap, ITE_I2CFDR); + buf[rdcount++] = tmp.byte[1]; + buf[rdcount++] = tmp.byte[0]; + } + + /* status FIFO underrun */ + iic_inw(adap, ITE_I2CFSR); + + } + + iic_stop(adap); + return rdcount; +} + + +/* This function implements combined transactions. Combined + * transactions consist of combinations of reading and writing blocks of data. + * Each transfer (i.e. a read or a write) is separated by a repeated start + * condition. + */ +#if 0 +static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + int i; + struct i2c_msg *pmsg; + int ret; + + DEB2(printk("Beginning combined transaction\n")); + + for(i=0; i<(num-1); i++) { + pmsg = &msgs[i]; + if(pmsg->flags & I2C_M_RD) { + DEB2(printk(" This one is a read\n")); + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER); + } + else if(!(pmsg->flags & I2C_M_RD)) { + DEB2(printk("This one is a write\n")); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER); + } + } + /* Last read or write segment needs to be terminated with a stop */ + pmsg = &msgs[i]; + + if(pmsg->flags & I2C_M_RD) { + DEB2(printk("Doing the last read\n")); + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); + } + else if(!(pmsg->flags & I2C_M_RD)) { + DEB2(printk("Doing the last write\n")); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); + } + + return ret; +} +#endif + + +/* Whenever we initiate a transaction, the first byte clocked + * onto the bus after the start condition is the address (7 bit) of the + * device we want to talk to. This function manipulates the address specified + * so that it makes sense to the hardware when written to the IIC peripheral. + * + * Note: 10 bit addresses are not supported in this driver, although they are + * supported by the hardware. This functionality needs to be implemented. + */ +static inline int iic_doAddress(struct i2c_algo_iic_data *adap, + struct i2c_msg *msg, int retries) +{ + unsigned short flags = msg->flags; + unsigned int addr; + int ret; + +/* Ten bit addresses not supported right now */ + if ( (flags & I2C_M_TEN) ) { +#if 0 + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at extended address code.\n"); + return -EREMOTEIO; + } + iic_outw(adap,msg->addr & 0x7f); + if (ret != 1) { + printk("iic_doAddress: died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at extended address code.\n"); + return -EREMOTEIO; + } + } +#endif + } else { + + addr = ( msg->addr << 1 ); + +#if 0 + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; +#endif + + if (iic_inw(adap, ITE_I2CSAR) != addr) { + iic_outw(adap, ITE_I2CSAR, addr); + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at address code.\n"); + return -EREMOTEIO; + } + } + + } + + return 0; +} + + +/* Description: Prepares the controller for a transaction (clearing status + * registers, data buffers, etc), and then calls either iic_readbytes or + * iic_sendbytes to do the actual transaction. + * + * still to be done: Before we issue a transaction, we should + * verify that the bus is not busy or in some unknown state. + */ +static int iic_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i = 0; + int ret, timeout; + + pmsg = &msgs[i]; + + if(!pmsg->len) { + DEB2(printk("iic_xfer: read/write length is 0\n");) + return -EIO; + } + if(!(pmsg->flags & I2C_M_RD) && (!(pmsg->len)%2) ) { + DEB2(printk("iic_xfer: write buffer length is not odd\n");) + return -EIO; + } + + /* Wait for any pending transfers to complete */ + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("iic_xfer: Timeout waiting for host not busy\n");) + return -EIO; + } + + /* Flush FIFO */ + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + + /* Load address */ + ret = iic_doAddress(adap, pmsg, i2c_adap->retries); + if (ret) + return -EIO; + +#if 0 + /* Combined transaction (read and write) */ + if(num > 1) { + DEB2(printk("iic_xfer: Call combined transaction\n")); + ret = iic_combined_transaction(i2c_adap, msgs, num); + } +#endif + + DEB3(printk("iic_xfer: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", + i, msgs[i].addr, msgs[i].flags, msgs[i].len);) + + if(pmsg->flags & I2C_M_RD) /* Read */ + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, 0); + else { /* Write */ + udelay(1000); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len); + } + + if (ret != pmsg->len) + DEB3(printk("iic_xfer: error or fail on read/write %d bytes.\n",ret)); + else + DEB3(printk("iic_xfer: read/write %d bytes.\n",ret)); + + return ret; +} + + +/* Implements device specific ioctls. Higher level ioctls can + * be found in i2c-core.c and are typical of any i2c controller (specifying + * slave address, timeouts, etc). These ioctls take advantage of any hardware + * features built into the controller for which this algorithm-adapter set + * was written. These ioctls allow you to take control of the data and clock + * lines and set the either high or low, + * similar to a GPIO pin. + */ +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + + struct i2c_algo_iic_data *adap = adapter->algo_data; + struct i2c_iic_msg s_msg; + char *buf; + int ret; + + if (cmd == I2C_SREAD) { + if(copy_from_user(&s_msg, (struct i2c_iic_msg *)arg, + sizeof(struct i2c_iic_msg))) + return -EFAULT; + buf = kmalloc(s_msg.len, GFP_KERNEL); + if (buf== NULL) + return -ENOMEM; + + /* Flush FIFO */ + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + + /* Load address */ + iic_outw(adap, ITE_I2CSAR,s_msg.addr<<1); + iic_outw(adap, ITE_I2CSSAR,s_msg.waddr & 0xff); + + ret = iic_readbytes(adapter, buf, s_msg.len, 1); + if (ret>=0) { + if(copy_to_user( s_msg.buf, buf, s_msg.len) ) + ret = -EFAULT; + } + kfree(buf); + } + return 0; +} + + +static u32 iic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm iic_algo = { + "ITE IIC algorithm", + I2C_ALGO_IIC, + iic_xfer, /* master_xfer */ + NULL, /* smbus_xfer */ + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + iic_func, /* functionality */ +}; + + +/* + * registering functions to load algorithms at runtime + */ +int i2c_iic_add_bus(struct i2c_adapter *adap) +{ + int i; + short status; + struct i2c_algo_iic_data *iic_adap = adap->algo_data; + + if (iic_test) { + int ret = test_bus(iic_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-ite: hw routines for %s registered.\n", + adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= iic_algo.id; + adap->algo = &iic_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + adap->flags = 0; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + i2c_add_adapter(adap); + iic_init(iic_adap); + + /* scan bus */ + /* By default scanning the bus is turned off. */ + if (iic_scan) { + printk(KERN_INFO " i2c-algo-ite: scanning bus %s.\n", + adap->name); + for (i = 0x00; i < 0xff; i+=2) { + iic_outw(iic_adap, ITE_I2CSAR, i); + iic_start(iic_adap); + if ( (wait_for_pin(iic_adap, &status) == 0) && + ((status & ITE_I2CHSR_DNE) == 0) ) { + printk(KERN_INFO "\n(%02x)\n",i>>1); + } else { + printk(KERN_INFO "."); + iic_reset(iic_adap); + } + udelay(iic_adap->udelay); + } + } + return 0; +} + + +int i2c_iic_del_bus(struct i2c_adapter *adap) +{ + int res; + if ((res = i2c_del_adapter(adap)) < 0) + return res; + DEB2(printk("i2c-algo-ite: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + + +int __init i2c_algo_iic_init (void) +{ + printk(KERN_INFO "ITE iic (i2c) algorithm module\n"); + return 0; +} + + +void i2c_algo_iic_exit(void) +{ + return; +} + + +EXPORT_SYMBOL(i2c_iic_add_bus); +EXPORT_SYMBOL(i2c_iic_del_bus); + +/* The MODULE_* macros resolve to nothing if MODULES is not defined + * when this file is compiled. + */ +MODULE_AUTHOR("MontaVista Software <www.mvista.com>"); +MODULE_DESCRIPTION("ITE iic algorithm"); + +MODULE_PARM(iic_test, "i"); +MODULE_PARM(iic_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available"); +MODULE_PARM_DESC(iic_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol"); + + +/* This function resolves to init_module (the function invoked when a module + * is loaded via insmod) when this file is compiled with MODULES defined. + * Otherwise (i.e. if you want this driver statically linked to the kernel), + * a pointer to this function is stored in a table and called + * during the intialization of the kernel (in do_basic_setup in /init/main.c) + * + * All this functionality is complements of the macros defined in linux/init.h + */ +module_init(i2c_algo_iic_init); + + +/* If MODULES is defined when this file is compiled, then this function will + * resolved to cleanup_module. + */ +module_exit(i2c_algo_iic_exit); diff --git a/drivers/i2c/i2c-ite.h b/drivers/i2c/i2c-ite.h new file mode 100644 index 000000000000..a8ca3c9b546a --- /dev/null +++ b/drivers/i2c/i2c-ite.h @@ -0,0 +1,117 @@ +/* + -------------------------------------------------------------------- + i2c-ite.h: Global defines for the I2C controller on board the + ITE MIPS processor. + -------------------------------------------------------------------- + Hai-Pao Fan, MontaVista Software, Inc. + hpfan@mvista.com or source@mvista.com + + Copyright 2001 MontaVista Software Inc. + + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#ifndef I2C_ITE_H +#define I2C_ITE_H 1 + +#include <asm/it8172/it8172.h> + +/* I2C Registers */ +#define ITE_I2CHCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x30 +#define ITE_I2CHSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x34 +#define ITE_I2CSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x38 +#define ITE_I2CSSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x3c +#define ITE_I2CCKCNT IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x48 +#define ITE_I2CSHDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x4c +#define ITE_I2CRSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x50 +#define ITE_I2CPSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x54 + +#define ITE_I2CFDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x70 +#define ITE_I2CFBCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x74 +#define ITE_I2CFCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x78 +#define ITE_I2CFSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x7c + + +/* Host Control Register ITE_I2CHCR */ +#define ITE_I2CHCR_HCE 0x01 /* Enable I2C Host Controller */ +#define ITE_I2CHCR_IE 0x02 /* Enable the interrupt after completing + the current transaction */ +#define ITE_I2CHCR_CP_W 0x00 /* bit2-4 000 - Write */ +#define ITE_I2CHCR_CP_R 0x08 /* 010 - Current address read */ +#define ITE_I2CHCR_CP_S 0x10 /* 100 - Sequential read */ +#define ITE_I2CHCR_ST 0x20 /* Initiates the I2C host controller to execute + the command and send the data programmed in + all required registers to I2C bus */ +#define ITE_CMD ITE_I2CHCR_HCE | ITE_I2CHCR_IE | ITE_I2CHCR_ST +#define ITE_WRITE ITE_CMD | ITE_I2CHCR_CP_W +#define ITE_READ ITE_CMD | ITE_I2CHCR_CP_R +#define ITE_SREAD ITE_CMD | ITE_I2CHCR_CP_S + +/* Host Status Register ITE_I2CHSR */ +#define ITE_I2CHSR_DB 0x01 /* Device is busy, receives NACK response except + in the first and last bytes */ +#define ITE_I2CHSR_DNE 0x02 /* Target address on I2C bus does not exist */ +#define ITE_I2CHSR_TDI 0x04 /* R/W Transaction on I2C bus was completed */ +#define ITE_I2CHSR_HB 0x08 /* Host controller is processing transactions */ +#define ITE_I2CHSR_FER 0x10 /* Error occurs in the FIFO */ + +/* Slave Address Register ITE_I2CSAR */ +#define ITE_I2CSAR_SA_MASK 0xfe /* Target I2C device address */ +#define ITE_I2CSAR_ASO 0x0100 /* Output 1/0 to I2CAS port when the + next slave address is addressed */ + +/* Slave Sub-address Register ITE_I2CSSAR */ +#define ITE_I2CSSAR_SUBA_MASK 0xff /* Target I2C device sub-address */ + +/* Clock Counter Register ITE_I2CCKCNT */ +#define ITE_I2CCKCNT_STOP 0x00 /* stop I2C clock */ +#define ITE_I2CCKCNT_HPCC_MASK 0x7f /* SCL high period counter */ +#define ITE_I2CCKCNT_LPCC_MASK 0x7f00 /* SCL low period counter */ + +/* START Hold Time Register ITE_I2CSHDR */ +/* value is counted based on 16 MHz internal clock */ +#define ITE_I2CSHDR_FM 0x0a /* START condition at fast mode */ +#define ITE_I2CSHDR_SM 0x47 /* START contition at standard mode */ + +/* (Repeated) START Setup Time Register ITE_I2CRSUR */ +/* value is counted based on 16 MHz internal clock */ +#define ITE_I2CRSUR_FM 0x0a /* repeated START condition at fast mode */ +#define ITE_I2CRSUR_SM 0x50 /* repeated START condition at standard mode */ + +/* STOP setup Time Register ITE_I2CPSUR */ + +/* FIFO Data Register ITE_I2CFDR */ +#define ITE_I2CFDR_MASK 0xff + +/* FIFO Byte Count Register ITE_I2CFBCR */ +#define ITE_I2CFBCR_MASK 0x3f + +/* FIFO Control Register ITE_I2CFCR */ +#define ITE_I2CFCR_FLUSH 0x01 /* Flush FIFO and reset the FIFO point + and I2CFSR */ +/* FIFO Status Register ITE_I2CFSR */ +#define ITE_I2CFSR_FO 0x01 /* FIFO is overrun when write */ +#define ITE_I2CFSR_FU 0x02 /* FIFO is underrun when read */ +#define ITE_I2CFSR_FF 0x04 /* FIFO is full when write */ +#define ITE_I2CFSR_FE 0x08 /* FIFO is empty when read */ + +#endif /* I2C_ITE_H */ diff --git a/drivers/i2o/i2o_block.c b/drivers/i2o/i2o_block.c index 3e8f813e532d..0ec403f178d4 100644 --- a/drivers/i2o/i2o_block.c +++ b/drivers/i2o/i2o_block.c @@ -1854,16 +1854,14 @@ static struct block_device_operations i2ob_fops = static struct gendisk i2ob_gendisk = { - MAJOR_NR, - "i2o/hd", - 4, - 1<<4, - i2ob, - i2ob_sizes, - MAX_I2OB, - NULL, - NULL, - &i2ob_fops, + major: MAJOR_NR, + major_name: "i2o/hd", + minor_shift: 4, + max_p: 1<<4, + part: i2ob, + sizes: i2ob_sizes, + nr_real: MAX_I2OB, + fops: &i2ob_fops, }; diff --git a/drivers/ide/amd7409.c b/drivers/ide/amd7409.c deleted file mode 100644 index d01a25f266fc..000000000000 --- a/drivers/ide/amd7409.c +++ /dev/null @@ -1,471 +0,0 @@ -/* - * linux/drivers/ide/amd7409.c Version 0.05 June 9, 2000 - * - * Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org> - * May be copied or modified under the terms of the GNU General Public License - * - */ - -#include <linux/config.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/timer.h> -#include <linux/mm.h> -#include <linux/ioport.h> -#include <linux/blkdev.h> -#include <linux/hdreg.h> - -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/ide.h> - -#include <asm/io.h> -#include <asm/irq.h> - -#include "ide_modes.h" - -#define DISPLAY_VIPER_TIMINGS - -#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) -#include <linux/stat.h> -#include <linux/proc_fs.h> - -static int amd7409_get_info(char *, char **, off_t, int); -extern int (*amd7409_display_info)(char *, char **, off_t, int); /* ide-proc.c */ -extern char *ide_media_verbose(ide_drive_t *); -static struct pci_dev *bmide_dev; - -static int amd7409_get_info (char *buffer, char **addr, off_t offset, int count) -{ - char *p = buffer; - u32 bibma = pci_resource_start(bmide_dev, 4); - u8 c0 = 0, c1 = 0; - - /* - * at that point bibma+0x2 et bibma+0xa are byte registers - * to investigate: - */ - c0 = inb_p((unsigned short)bibma + 0x02); - c1 = inb_p((unsigned short)bibma + 0x0a); - - p += sprintf(p, "\n AMD 7409 VIPER Chipset.\n"); - p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n"); - p += sprintf(p, " %sabled %sabled\n", - (c0&0x80) ? "dis" : " en", - (c1&0x80) ? "dis" : " en"); - p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n"); - p += sprintf(p, "DMA enabled: %s %s %s %s\n", - (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ", - (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " ); - p += sprintf(p, "UDMA\n"); - p += sprintf(p, "DMA\n"); - p += sprintf(p, "PIO\n"); - - return p-buffer; /* => must be less than 4k! */ -} -#endif /* defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) */ - -byte amd7409_proc = 0; - -extern char *ide_xfer_verbose (byte xfer_rate); - -static unsigned int amd7409_swdma_check (struct pci_dev *dev) -{ - unsigned int class_rev; - pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); - class_rev &= 0xff; - return ((int) (class_rev >= 7) ? 1 : 0); -} - -static int amd7409_swdma_error(ide_drive_t *drive) -{ - printk("%s: single-word DMA not support (revision < C4)\n", drive->name); - return 0; -} - -/* - * Here is where all the hard work goes to program the chipset. - * - */ -static int amd7409_tune_chipset (ide_drive_t *drive, byte speed) -{ - ide_hwif_t *hwif = HWIF(drive); - struct pci_dev *dev = hwif->pci_dev; - int err = 0; - byte unit = (drive->select.b.unit & 0x01); -#ifdef CONFIG_BLK_DEV_IDEDMA - unsigned long dma_base = hwif->dma_base; -#endif /* CONFIG_BLK_DEV_IDEDMA */ - byte drive_pci = 0x00; - byte drive_pci2 = 0x00; - byte ultra_timing = 0x00; - byte dma_pio_timing = 0x00; - byte pio_timing = 0x00; - - switch (drive->dn) { - case 0: drive_pci = 0x53; drive_pci2 = 0x4b; break; - case 1: drive_pci = 0x52; drive_pci2 = 0x4a; break; - case 2: drive_pci = 0x51; drive_pci2 = 0x49; break; - case 3: drive_pci = 0x50; drive_pci2 = 0x48; break; - default: - return -1; - } - - pci_read_config_byte(dev, drive_pci, &ultra_timing); - pci_read_config_byte(dev, drive_pci2, &dma_pio_timing); - pci_read_config_byte(dev, 0x4c, &pio_timing); - -#ifdef DEBUG - printk("%s: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ", - drive->name, ultra_timing, dma_pio_timing, pio_timing); -#endif - - ultra_timing &= ~0xC7; - dma_pio_timing &= ~0xFF; - pio_timing &= ~(0x03 << drive->dn); - -#ifdef DEBUG - printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x ", - ultra_timing, dma_pio_timing, pio_timing); -#endif - - switch(speed) { -#ifdef CONFIG_BLK_DEV_IDEDMA - case XFER_UDMA_4: - ultra_timing |= 0x45; - dma_pio_timing |= 0x20; - break; - case XFER_UDMA_3: - ultra_timing |= 0x44; - dma_pio_timing |= 0x20; - break; - case XFER_UDMA_2: - ultra_timing |= 0x40; - dma_pio_timing |= 0x20; - break; - case XFER_UDMA_1: - ultra_timing |= 0x41; - dma_pio_timing |= 0x20; - break; - case XFER_UDMA_0: - ultra_timing |= 0x42; - dma_pio_timing |= 0x20; - break; - case XFER_MW_DMA_2: - dma_pio_timing |= 0x20; - break; - case XFER_MW_DMA_1: - dma_pio_timing |= 0x21; - break; - case XFER_MW_DMA_0: - dma_pio_timing |= 0x77; - break; - case XFER_SW_DMA_2: - if (!amd7409_swdma_check(dev)) - return amd7409_swdma_error(drive); - dma_pio_timing |= 0x42; - break; - case XFER_SW_DMA_1: - if (!amd7409_swdma_check(dev)) - return amd7409_swdma_error(drive); - dma_pio_timing |= 0x65; - break; - case XFER_SW_DMA_0: - if (!amd7409_swdma_check(dev)) - return amd7409_swdma_error(drive); - dma_pio_timing |= 0xA8; - break; -#endif /* CONFIG_BLK_DEV_IDEDMA */ - case XFER_PIO_4: - dma_pio_timing |= 0x20; - break; - case XFER_PIO_3: - dma_pio_timing |= 0x22; - break; - case XFER_PIO_2: - dma_pio_timing |= 0x42; - break; - case XFER_PIO_1: - dma_pio_timing |= 0x65; - break; - case XFER_PIO_0: - default: - dma_pio_timing |= 0xA8; - break; - } - - pio_timing |= (0x03 << drive->dn); - - if (!drive->init_speed) - drive->init_speed = speed; - -#ifdef CONFIG_BLK_DEV_IDEDMA - pci_write_config_byte(dev, drive_pci, ultra_timing); -#endif /* CONFIG_BLK_DEV_IDEDMA */ - pci_write_config_byte(dev, drive_pci2, dma_pio_timing); - pci_write_config_byte(dev, 0x4c, pio_timing); - -#ifdef DEBUG - printk(":: UDMA 0x%02x DMAPIO 0x%02x PIO 0x%02x\n", - ultra_timing, dma_pio_timing, pio_timing); -#endif - -#ifdef CONFIG_BLK_DEV_IDEDMA - if (speed > XFER_PIO_4) { - outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2); - } else { - outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2); - } -#endif /* CONFIG_BLK_DEV_IDEDMA */ - - err = ide_config_drive_speed(drive, speed); - drive->current_speed = speed; - return (err); -} - -static void config_chipset_for_pio (ide_drive_t *drive) -{ - unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90}; - unsigned short xfer_pio = drive->id->eide_pio_modes; - byte timing, speed, pio; - - pio = ide_get_best_pio_mode(drive, 255, 5, NULL); - - if (xfer_pio> 4) - xfer_pio = 0; - - if (drive->id->eide_pio_iordy > 0) { - for (xfer_pio = 5; - xfer_pio>0 && - drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio]; - xfer_pio--); - } else { - xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 : - (drive->id->eide_pio_modes & 2) ? 0x04 : - (drive->id->eide_pio_modes & 1) ? 0x03 : - (drive->id->tPIO & 2) ? 0x02 : - (drive->id->tPIO & 1) ? 0x01 : xfer_pio; - } - - timing = (xfer_pio >= pio) ? xfer_pio : pio; - - switch(timing) { - case 4: speed = XFER_PIO_4;break; - case 3: speed = XFER_PIO_3;break; - case 2: speed = XFER_PIO_2;break; - case 1: speed = XFER_PIO_1;break; - default: - speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW; - break; - } - (void) amd7409_tune_chipset(drive, speed); - drive->current_speed = speed; -} - -static void amd7409_tune_drive (ide_drive_t *drive, byte pio) -{ - byte speed; - switch(pio) { - case 4: speed = XFER_PIO_4;break; - case 3: speed = XFER_PIO_3;break; - case 2: speed = XFER_PIO_2;break; - case 1: speed = XFER_PIO_1;break; - default: speed = XFER_PIO_0;break; - } - (void) amd7409_tune_chipset(drive, speed); -} - -#ifdef CONFIG_BLK_DEV_IDEDMA -/* - * This allows the configuration of ide_pci chipset registers - * for cards that learn about the drive's UDMA, DMA, PIO capabilities - * after the drive is reported by the OS. - */ -static int config_chipset_for_dma (ide_drive_t *drive) -{ - struct hd_driveid *id = drive->id; - byte udma_66 = eighty_ninty_three(drive); - byte udma_100 = 0; - byte speed = 0x00; - int rval; - - if ((id->dma_ultra & 0x0020) && (udma_66)&& (udma_100)) { - speed = XFER_UDMA_5; - } else if ((id->dma_ultra & 0x0010) && (udma_66)) { - speed = XFER_UDMA_4; - } else if ((id->dma_ultra & 0x0008) && (udma_66)) { - speed = XFER_UDMA_3; - } else if (id->dma_ultra & 0x0004) { - speed = XFER_UDMA_2; - } else if (id->dma_ultra & 0x0002) { - speed = XFER_UDMA_1; - } else if (id->dma_ultra & 0x0001) { - speed = XFER_UDMA_0; - } else if (id->dma_mword & 0x0004) { - speed = XFER_MW_DMA_2; - } else if (id->dma_mword & 0x0002) { - speed = XFER_MW_DMA_1; - } else if (id->dma_mword & 0x0001) { - speed = XFER_MW_DMA_0; - } else { - return ((int) ide_dma_off_quietly); - } - - (void) amd7409_tune_chipset(drive, speed); - - rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on : - ((id->dma_ultra >> 8) & 7) ? ide_dma_on : - ((id->dma_mword >> 8) & 7) ? ide_dma_on : - ide_dma_off_quietly); - - return rval; -} - - - -static int config_drive_xfer_rate (ide_drive_t *drive) -{ - struct hd_driveid *id = drive->id; - ide_dma_action_t dma_func = ide_dma_on; - - if (id && (id->capability & 1) && HWIF(drive)->autodma) { - /* Consult the list of known "bad" drives */ - if (ide_dmaproc(ide_dma_bad_drive, drive)) { - dma_func = ide_dma_off; - goto fast_ata_pio; - } - dma_func = ide_dma_off_quietly; - if (id->field_valid & 4) { - if (id->dma_ultra & 0x002F) { - /* Force if Capable UltraDMA */ - dma_func = config_chipset_for_dma(drive); - if ((id->field_valid & 2) && - (dma_func != ide_dma_on)) - goto try_dma_modes; - } - } else if (id->field_valid & 2) { -try_dma_modes: - if ((id->dma_mword & 0x0007) || - ((id->dma_1word & 0x007) && - (amd7409_swdma_check(HWIF(drive)->pci_dev)))) { - /* Force if Capable regular DMA modes */ - dma_func = config_chipset_for_dma(drive); - if (dma_func != ide_dma_on) - goto no_dma_set; - } - - } else if (ide_dmaproc(ide_dma_good_drive, drive)) { - if (id->eide_dma_time > 150) { - goto no_dma_set; - } - /* Consult the list of known "good" drives */ - dma_func = config_chipset_for_dma(drive); - if (dma_func != ide_dma_on) - goto no_dma_set; - } else { - goto fast_ata_pio; - } - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: - dma_func = ide_dma_off_quietly; -no_dma_set: - - config_chipset_for_pio(drive); - } - return HWIF(drive)->dmaproc(dma_func, drive); -} - -/* - * amd7409_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. - */ - -int amd7409_dmaproc (ide_dma_action_t func, ide_drive_t *drive) -{ - switch (func) { - case ide_dma_check: - return config_drive_xfer_rate(drive); - default: - break; - } - return ide_dmaproc(func, drive); /* use standard DMA stuff */ -} -#endif /* CONFIG_BLK_DEV_IDEDMA */ - -unsigned int __init pci_init_amd7409 (struct pci_dev *dev, const char *name) -{ - unsigned long fixdma_base = pci_resource_start(dev, 4); - -#ifdef CONFIG_BLK_DEV_IDEDMA - if (!amd7409_swdma_check(dev)) - printk("%s: disabling single-word DMA support (revision < C4)\n", name); -#endif /* CONFIG_BLK_DEV_IDEDMA */ - - if (!fixdma_base) { - /* - * - */ - } else { - /* - * enable DMA capable bit, and "not" simplex only - */ - outb(inb(fixdma_base+2) & 0x60, fixdma_base+2); - - if (inb(fixdma_base+2) & 0x80) - printk("%s: simplex device: DMA will fail!!\n", name); - } -#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) - if (!amd7409_proc) { - amd7409_proc = 1; - bmide_dev = dev; - amd7409_display_info = &amd7409_get_info; - } -#endif /* DISPLAY_VIPER_TIMINGS && CONFIG_PROC_FS */ - - return 0; -} - -unsigned int __init ata66_amd7409 (ide_hwif_t *hwif) -{ -#ifdef CONFIG_AMD7409_OVERRIDE - byte ata66 = 1; -#else - byte ata66 = 0; -#endif /* CONFIG_AMD7409_OVERRIDE */ - -#if 0 - pci_read_config_byte(hwif->pci_dev, 0x48, &ata66); - return ((ata66 & 0x02) ? 0 : 1); -#endif - return ata66; -} - -void __init ide_init_amd7409 (ide_hwif_t *hwif) -{ - hwif->tuneproc = &amd7409_tune_drive; - hwif->speedproc = &amd7409_tune_chipset; - -#ifndef CONFIG_BLK_DEV_IDEDMA - hwif->drives[0].autotune = 1; - hwif->drives[1].autotune = 1; - hwif->autodma = 0; - return; -#else - - if (hwif->dma_base) { - hwif->dmaproc = &amd7409_dmaproc; - if (!noautodma) - hwif->autodma = 1; - } else { - hwif->autodma = 0; - hwif->drives[0].autotune = 1; - hwif->drives[1].autotune = 1; - } -#endif /* CONFIG_BLK_DEV_IDEDMA */ -} - -void __init ide_dmacapable_amd7409 (ide_hwif_t *hwif, unsigned long dmabase) -{ - ide_setup_dma(hwif, dmabase, 8); -} diff --git a/drivers/ide/hd.c b/drivers/ide/hd.c index ab816436b09c..bdc854f46a3a 100644 --- a/drivers/ide/hd.c +++ b/drivers/ide/hd.c @@ -688,16 +688,13 @@ static int hd_release(struct inode * inode, struct file * file) extern struct block_device_operations hd_fops; static struct gendisk hd_gendisk = { - MAJOR_NR, /* Major number */ - "hd", /* Major name */ - 6, /* Bits to shift to get real from partition */ - 1 << 6, /* Number of partitions per real */ - hd, /* hd struct */ - hd_sizes, /* block sizes */ - 0, /* number */ - NULL, /* internal use, not presently used */ - NULL, /* next */ - &hd_fops, /* file operations */ + major: MAJOR_NR, + major_name: "hd", + minor_shift: 6, + max_p: 1 << 6, + part: hd, + sizes: hd_sizes, + fops: &hd_fops, }; static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -842,7 +839,7 @@ int __init hd_init(void) } blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ - add_gendisk(&hd_gendisk, MAJOR_NR); + add_gendisk(&hd_gendisk); init_timer(&device_timer); device_timer.function = hd_times_out; hd_geninit(); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index c92dbaaf1ed6..3ad4c48e61ce 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -130,10 +130,30 @@ struct drive_list_entry drive_blacklist [] = { { "WDC AC23200L" , "21.10N21" }, { "Compaq CRD-8241B" , "ALL" }, { "CRD-8400B" , "ALL" }, + { "CRD-8480B", "ALL" }, + { "CRD-8480C", "ALL" }, + { "CRD-8482B", "ALL" }, + { "CRD-84" , "ALL" }, + { "SanDisk SDP3B" , "ALL" }, + { "SanDisk SDP3B-64" , "ALL" }, + { "SANYO CD-ROM CRD" , "ALL" }, + { "HITACHI CDR-8" , "ALL" }, + { "HITACHI CDR-8335" , "ALL" }, + { "HITACHI CDR-8435" , "ALL" }, + { "Toshiba CD-ROM XM-6202B" , "ALL" }, + { "CD-532E-A" , "ALL" }, + { "E-IDE CD-ROM CR-840", "ALL" }, + { "CD-ROM Drive/F5A", "ALL" }, + { "RICOH CD-R/RW MP7083A", "ALL" }, + { "WPI CDD-820", "ALL" }, + { "SAMSUNG CD-ROM SC-148C", "ALL" }, + { "SAMSUNG CD-ROM SC-148F", "ALL" }, + { "SAMSUNG CD-ROM SC", "ALL" }, { "SanDisk SDP3B-64" , "ALL" }, { "SAMSUNG CD-ROM SN-124", "ALL" }, { "PLEXTOR CD-R PX-W8432T", "ALL" }, { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, + { "_NEC DV5800A", "ALL" }, { 0 , 0 } }; diff --git a/drivers/ide/ide-pci.c b/drivers/ide/ide-pci.c index 506ff25f38d7..8f473d078bd7 100644 --- a/drivers/ide/ide-pci.c +++ b/drivers/ide/ide-pci.c @@ -38,6 +38,7 @@ #define DEVID_PIIX4U3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9}) #define DEVID_PIIX4U4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8}) #define DEVID_VIA_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561}) +#define DEVID_MR_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1}) #define DEVID_VP_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1}) #define DEVID_PDC20246 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246}) #define DEVID_PDC20262 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262}) @@ -385,6 +386,7 @@ static ide_pci_device_t ide_pci_chipsets[] __initdata = { {DEVID_PIIX4U3, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_PIIX4U4, "PIIX4", PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, {DEVID_VIA_IDE, "VIA_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_MR_IDE, "VP_IDE", PCI_VIA82CXXX, ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0 }, {DEVID_VP_IDE, "VP_IDE", PCI_VIA82CXXX, ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0 }, #ifdef CONFIG_PDC202XX_FORCE {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 16 }, @@ -758,6 +760,7 @@ controller_ok: IDE_PCI_DEVID_EQ(d->devid, DEVID_PIIX4NX) || IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) || IDE_PCI_DEVID_EQ(d->devid, DEVID_VIA_IDE) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_MR_IDE) || IDE_PCI_DEVID_EQ(d->devid, DEVID_VP_IDE)) autodma = 0; if (autodma) @@ -767,6 +770,7 @@ controller_ok: IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265) || IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20267) || IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268R) || IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) || IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260) || IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260R) || diff --git a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c index c846f7811eb9..a09ba75455a2 100644 --- a/drivers/ide/serverworks.c +++ b/drivers/ide/serverworks.c @@ -544,8 +544,33 @@ unsigned int __init pci_init_svwks (struct pci_dev *dev, const char *name) return 0; } +/* On Dell PowerEdge servers with a CSB5, the top two bits of the subsystem + * device ID indicate presence of an 80-pin cable. + * Bit 15 clear = secondary IDE channel does not have 80-pin cable. + * Bit 15 set = secondary IDE channel has 80-pin cable. + * Bit 14 clear = primary IDE channel does not have 80-pin cable. + * Bit 14 set = primary IDE channel has 80-pin cable. + */ + +static unsigned int __init ata66_svwks_dell (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL && + dev->vendor == PCI_VENDOR_ID_SERVERWORKS && + dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) + return ((1 << (hwif->channel + 14)) & + dev->subsystem_device) ? 1 : 0; + + return 0; + +} + unsigned int __init ata66_svwks (ide_hwif_t *hwif) { + struct pci_dev *dev = hwif->pci_dev; + if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL) + return ata66_svwks_dell (hwif); + return 0; } diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index 8872b4c8a93c..30ccb26c32e8 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -76,8 +76,11 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool ' Am7930' CONFIG_HISAX_AMD7930 fi fi + bool ' HiSax debugging' CONFIG_HISAX_DEBUG + dep_tristate 'Sedlbauer PCMCIA cards' CONFIG_HISAX_SEDLBAUER_CS $CONFIG_PCMCIA dep_tristate 'ELSA PCMCIA MicroLink cards' CONFIG_HISAX_ELSA_CS $CONFIG_PCMCIA + dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL fi endmenu diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c index c39f9d5df626..889161b870e7 100644 --- a/drivers/isdn/act2000/act2000_isa.c +++ b/drivers/isdn/act2000/act2000_isa.c @@ -418,7 +418,7 @@ act2000_isa_getid(act2000_card * card) int act2000_isa_download(act2000_card * card, act2000_ddef * cb) { - int length; + unsigned int length; int ret; int l; int c; @@ -431,9 +431,8 @@ act2000_isa_download(act2000_card * card, act2000_ddef * cb) if (!act2000_isa_reset(card->port)) return -ENXIO; act2000_isa_delay(HZ / 2); - if ((ret = verify_area(VERIFY_READ, (void *) cb, sizeof(cblock)))) - return ret; - copy_from_user(&cblock, (char *) cb, sizeof(cblock)); + if(copy_from_user(&cblock, (char *) cb, sizeof(cblock))) + return -EFAULT; length = cblock.length; p = cblock.buffer; if ((ret = verify_area(VERIFY_READ, (void *) p, length))) diff --git a/drivers/isdn/eicon/idi.c b/drivers/isdn/eicon/idi.c index 1bb57a77924a..62b4342f70d8 100644 --- a/drivers/isdn/eicon/idi.c +++ b/drivers/isdn/eicon/idi.c @@ -431,7 +431,9 @@ void DivasOut(ADAPTER * a) i = this->XCurrent; X = PTR_X(a,this); while(i<this->XNum && length<270) { - clength = MIN((word)(270-length),X[i].PLength-this->XOffset); + clength = (word)(270-length); + if (clength > X[i].PLength-this->XOffset) + clength = X[i].PLength-this->XOffset; a->ram_out_buffer(a, &ReqOut->XBuffer.P[length], PTR_P(a,this,&X[i].P[this->XOffset]), @@ -837,8 +839,9 @@ byte isdn_ind(ADAPTER * a, this->ROffset = 0; this->RCurrent++; } - clength = MIN(a->ram_inw(a, &RBuffer->length)-offset, - R[this->RCurrent].PLength-this->ROffset); + clength = a->ram_inw(a, &RBuffer->length)-offset; + if (clength > R[this->RCurrent].PLength-this->ROffset) + clength = R[this->RCurrent].PLength-this->ROffset; if(R[this->RCurrent].P) { a->ram_in_buffer(a, &RBuffer->P[offset], diff --git a/drivers/isdn/eicon/kprintf.c b/drivers/isdn/eicon/kprintf.c index 9a4598fc6abb..3357bca2666a 100644 --- a/drivers/isdn/eicon/kprintf.c +++ b/drivers/isdn/eicon/kprintf.c @@ -30,8 +30,6 @@ #include "eicon.h" #include "sys.h" #include <stdarg.h> -#undef MAX -#undef MIN #include "divas.h" #include "divalog.h" diff --git a/drivers/isdn/eicon/pc.h b/drivers/isdn/eicon/pc.h index 0ba821ad2600..1adfe4dac4bc 100644 --- a/drivers/isdn/eicon/pc.h +++ b/drivers/isdn/eicon/pc.h @@ -29,12 +29,6 @@ #define byte unsigned char #define word unsigned short #define dword unsigned long -#if !defined(MIN) -#define MIN(a,b) ((a)>(b) ? (b) : (a)) -#endif -#if !defined(MAX) -#define MAX(a,b) ((a)>(b) ? (a) : (b)) -#endif /*------------------------------------------------------------------*/ /* buffer definition */ diff --git a/drivers/isdn/eicon/sys.h b/drivers/isdn/eicon/sys.h index e5141b856b04..3a69dbba063e 100644 --- a/drivers/isdn/eicon/sys.h +++ b/drivers/isdn/eicon/sys.h @@ -54,15 +54,6 @@ typedef volatile dword vdword; #define NULL ((void *) 0) #endif -/* MIN and MAX */ - -#if !defined(MIN) -#define MIN(a,b) ((a)>(b) ? (b) : (a)) -#endif -#if !defined(MAX) -#define MAX(a,b) ((a)>(b) ? (a) : (b)) -#endif - /* Return the dimension of an array */ #if !defined(DIM) @@ -85,6 +76,8 @@ void HwFatalError(void); /* void HwAssert(char *file, int line, char *condition); */ #include <linux/kernel.h> +#include <linux/string.h> + #define _PRINTK printk #define _PRINTF DivasPrintf diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index 3d79592d565d..9f1a1f657812 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -2,20 +2,21 @@ # The target object and module list name. -O_TARGET := vmlinux-obj.o +O_TARGET := vmlinux-obj.o # Objects that export symbols. -export-objs := config.o +export-objs := config.o fsm.o # Multipart objects. -list-multi := hisax.o -hisax-objs := config.o isdnl1.o tei.o isdnl2.o isdnl3.o \ - lmgr.o q931.o callc.o fsm.o cert.o +list-multi := hisax.o hisax_st5481.o +hisax-objs := config.o isdnl1.o tei.o isdnl2.o isdnl3.o \ + lmgr.o q931.o callc.o fsm.o cert.o +hisax_st5481-objs := st5481_init.o st5481_usb.o st5481_d.o st5481_b.o \ + st5481_hdlc.o # Optional parts of multipart objects. - hisax-objs-$(CONFIG_HISAX_EURO) += l3dss1.o hisax-objs-$(CONFIG_HISAX_NI1) += l3ni1.o hisax-objs-$(CONFIG_HISAX_1TR6) += l3_1tr6.o @@ -27,7 +28,6 @@ hisax-objs-$(CONFIG_HISAX_S0BOX) += s0box.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_AVM_A1) += avm_a1.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_AVM_A1_PCMCIA) += avm_a1p.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_FRITZPCI) += avm_pci.o isac.o arcofi.o -hisax-objs-$(CONFIG_HISAX_AVM_A1) += avm_a1.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_ELSA) += elsa.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_IX1MICROR2) += ix1_micro.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_DIEHLDIVA) += diva.o isac.o arcofi.o hscx.o @@ -54,9 +54,10 @@ hisax-objs += $(sort $(hisax-objs-y)) # Each configuration option enables a list of files. -obj-$(CONFIG_ISDN_DRV_HISAX) += hisax.o +obj-$(CONFIG_ISDN_DRV_HISAX) += hisax.o obj-$(CONFIG_HISAX_SEDLBAUER_CS) += sedlbauer_cs.o -obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o +obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o +obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) CFLAGS_cert.o := -DCERTIFICATION=$(CERT) @@ -67,3 +68,6 @@ include $(TOPDIR)/Rules.make hisax.o: $(hisax-objs) $(LD) -r -o $@ $(hisax-objs) + +hisax_st5481.o: $(hisax_st5481-objs) + $(LD) -r -o $@ $(hisax_st5481-objs) diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index 2f25292320e7..66087a18e119 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 2.51.6.4 2001/06/09 15:14:17 kai Exp $ +/* $Id: callc.c,v 2.51.6.5 2001/08/23 19:44:23 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -20,12 +20,12 @@ #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module)) #endif /* MODULE */ -const char *lli_revision = "$Revision: 2.51.6.4 $"; +const char *lli_revision = "$Revision: 2.51.6.5 $"; extern struct IsdnCard cards[]; extern int nrcards; -extern void HiSax_mod_dec_use_count(void); -extern void HiSax_mod_inc_use_count(void); +extern void HiSax_mod_dec_use_count(struct IsdnCardState *cs); +extern void HiSax_mod_inc_use_count(struct IsdnCardState *cs); static int init_b_st(struct Channel *chanp, int incoming); static void release_b_st(struct Channel *chanp); @@ -1584,7 +1584,7 @@ HiSax_command(isdn_ctrl * ic) } break; case (ISDN_CMD_LOCK): - HiSax_mod_inc_use_count(); + HiSax_mod_inc_use_count(csta); #ifdef MODULE if (csta->channel[0].debug & 0x400) HiSax_putstatus(csta, " LOCK ", "modcnt %lx", @@ -1592,7 +1592,7 @@ HiSax_command(isdn_ctrl * ic) #endif /* MODULE */ break; case (ISDN_CMD_UNLOCK): - HiSax_mod_dec_use_count(); + HiSax_mod_dec_use_count(csta); #ifdef MODULE if (csta->channel[0].debug & 0x400) HiSax_putstatus(csta, " UNLOCK ", "modcnt %lx", @@ -1624,11 +1624,11 @@ HiSax_command(isdn_ctrl * ic) break; case (3): for (i = 0; i < *(unsigned int *) ic->parm.num; i++) - HiSax_mod_dec_use_count(); + HiSax_mod_dec_use_count(NULL); break; case (4): for (i = 0; i < *(unsigned int *) ic->parm.num; i++) - HiSax_mod_inc_use_count(); + HiSax_mod_inc_use_count(NULL); break; case (5): /* set card in leased mode */ num = *(unsigned int *) ic->parm.num; @@ -1698,7 +1698,7 @@ HiSax_command(isdn_ctrl * ic) #ifdef MODULE case (55): MOD_USE_COUNT = 0; - HiSax_mod_inc_use_count(); + HiSax_mod_inc_use_count(NULL); break; #endif /* MODULE */ case (11): @@ -1810,6 +1810,7 @@ HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb) cli(); nskb = skb_clone(skb, GFP_ATOMIC); if (nskb) { + nskb->truesize = nskb->len; if (!ack) nskb->pkt_type = PACKET_NOACK; if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I) diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 852ef1a54407..7a2db6d34384 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,4 +1,4 @@ -/* $Id: config.c,v 2.57.6.16 2001/07/13 09:01:00 kai Exp $ +/* $Id: config.c,v 2.57.6.18 2001/08/27 22:19:05 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -313,7 +313,8 @@ EXPORT_SYMBOL(hfc_init_pcmcia); #define DEFAULT_PROTO_NAME "UNKNOWN" #endif #ifndef DEFAULT_CARD -#error "HiSax: No cards configured" +#define DEFAULT_CARD 0 +#define DEFAULT_CFG {0,0,0,0} #endif int hisax_init_pcmcia(void *, int *, struct IsdnCard *); @@ -423,16 +424,6 @@ void __init HiSaxVersion(void) certification_check(1); } -void HiSax_mod_dec_use_count(void) -{ - MOD_DEC_USE_COUNT; -} - -void HiSax_mod_inc_use_count(void) -{ - MOD_INC_USE_COUNT; -} - #ifndef MODULE #define MAX_ARG (HISAX_MAX_CARDS*5) static int __init HiSax_setup(char *line) @@ -840,8 +831,9 @@ static void closecard(int cardnr) if (csta->DC_Close != NULL) { csta->DC_Close(csta); } - csta->cardmsg(csta, CARD_RELEASE, NULL); - if (csta->dbusytimer.function != NULL) + if (csta->cardmsg) + csta->cardmsg(csta, CARD_RELEASE, NULL); + if (csta->dbusytimer.function != NULL) // FIXME? del_timer(&csta->dbusytimer); ll_unload(csta); } @@ -1137,6 +1129,9 @@ static int __devinit checkcard(int cardnr, char *id, int *busy_flag) ret = setup_netjet_u(card); break; #endif + case ISDN_CTYPE_DYNAMIC: + ret = 2; + break; default: printk(KERN_WARNING "HiSax: Support for %s Card not selected\n", @@ -1165,7 +1160,17 @@ static int __devinit checkcard(int cardnr, char *id, int *busy_flag) init_bcstate(cs, 0); init_bcstate(cs, 1); - ret = init_card(cs); + + /* init_card only handles interrupts which are not */ + /* used here for the loadable driver */ + switch (card->typ) { + case ISDN_CTYPE_DYNAMIC: + ret = 0; + break; + default: + ret = init_card(cs); + break; + } if (ret) { closecard(cardnr); ret = 0; @@ -1343,6 +1348,8 @@ static int __init HiSax_init(void) #ifdef MODULE if (!type[0]) { /* We 'll register drivers later, but init basic functions */ + for (i = 0; i < HISAX_MAX_CARDS; i++) + cards[i].typ = 0; return 0; } #ifdef CONFIG_HISAX_ELSA @@ -1572,7 +1579,6 @@ int hfc_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) { #ifdef MODULE int i; - int nzproto = 0; nrcards = 0; /* Initialize all structs, even though we only accept @@ -1584,14 +1590,12 @@ int hfc_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) cards[i].typ = type[i]; if (protocol[i]) { cards[i].protocol = protocol[i]; - nzproto++; } } cards[0].para[0] = pcm_irq; cards[0].para[1] = (int) pcm_iob; cards[0].protocol = prot; cards[0].typ = ISDN_CTYPE_HFC_SP_PCMCIA; - nzproto = 1; if (!HiSax_id) HiSax_id = HiSaxID; @@ -1615,7 +1619,6 @@ int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) { #ifdef MODULE int i; - int nzproto = 0; nrcards = 0; /* Initialize all structs, even though we only accept @@ -1627,14 +1630,12 @@ int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) cards[i].typ = type[i]; if (protocol[i]) { cards[i].protocol = protocol[i]; - nzproto++; } } cards[0].para[0] = pcm_irq; cards[0].para[1] = (int) pcm_iob; cards[0].protocol = prot; cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA; - nzproto = 1; if (!HiSax_id) HiSax_id = HiSaxID; @@ -1658,7 +1659,6 @@ int avm_a1_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) { #ifdef MODULE int i; - int nzproto = 0; nrcards = 0; /* Initialize all structs, even though we only accept @@ -1670,14 +1670,12 @@ int avm_a1_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) cards[i].typ = type[i]; if (protocol[i]) { cards[i].protocol = protocol[i]; - nzproto++; } } cards[0].para[0] = pcm_irq; cards[0].para[1] = (int) pcm_iob; cards[0].protocol = prot; cards[0].typ = ISDN_CTYPE_A1_PCMCIA; - nzproto = 1; if (!HiSax_id) HiSax_id = HiSaxID; @@ -1715,11 +1713,362 @@ int __devinit hisax_init_pcmcia(void *pcm_iob, int *busy_flag, return ret; } +#include "hisax_if.h" + +EXPORT_SYMBOL(hisax_register); +EXPORT_SYMBOL(hisax_unregister); + +static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg); +static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg); +static void hisax_d_l2l1(struct PStack *st, int pr, void *arg); +static void hisax_b_l2l1(struct PStack *st, int pr, void *arg); +static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg); +static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs); +static void hisax_bc_close(struct BCState *bcs); +static void hisax_bh(struct IsdnCardState *cs); +static void EChannel_proc_rcv(struct hisax_d_if *d_if); + +int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[], + char *name, int protocol) +{ + int i, retval; + char id[20]; + struct IsdnCardState *cs; + + for (i = 0; i < HISAX_MAX_CARDS; i++) { + if (!cards[i].typ) + break; + } + + if (i >= HISAX_MAX_CARDS) + return -EBUSY; + + cards[i].typ = ISDN_CTYPE_DYNAMIC; + cards[i].protocol = protocol; + sprintf(id, "%s%d", name, i); + nrcards++; + retval = checkcard(i, id, 0); + if (retval == 0) { // yuck + cards[i].typ = 0; + nrcards--; + return retval; + } + cs = cards[i].cs; + hisax_d_if->cs = cs; + cs->hw.hisax_d_if = hisax_d_if; + cs->cardmsg = hisax_cardmsg; + cs->tqueue.routine = (void *) (void *) hisax_bh; + cs->channel[0].d_st->l2.l2l1 = hisax_d_l2l1; + for (i = 0; i < 2; i++) { + cs->bcs[i].BC_SetStack = hisax_bc_setstack; + cs->bcs[i].BC_Close = hisax_bc_close; + + b_if[i]->ifc.l1l2 = hisax_b_l1l2; + + hisax_d_if->b_if[i] = b_if[i]; + } + hisax_d_if->ifc.l1l2 = hisax_d_l1l2; + skb_queue_head_init(&hisax_d_if->erq); + clear_bit(0, &hisax_d_if->ph_state); + + return 0; +} + +void hisax_unregister(struct hisax_d_if *hisax_d_if) +{ + cards[hisax_d_if->cs->cardnr].typ = 0; + HiSax_closecard(hisax_d_if->cs->cardnr); + skb_queue_purge(&hisax_d_if->erq); +} + +#include "isdnl1.h" + +static void hisax_sched_event(struct IsdnCardState *cs, int event) +{ + cs->event |= 1 << event; + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void hisax_bh(struct IsdnCardState *cs) +{ + struct PStack *st; + int pr; + + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(E_RCVBUFREADY, &cs->event)) + EChannel_proc_rcv(cs->hw.hisax_d_if); + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + if (test_bit(0, &cs->hw.hisax_d_if->ph_state)) + pr = PH_ACTIVATE | INDICATION; + else + pr = PH_DEACTIVATE | INDICATION; + for (st = cs->stlist; st; st = st->next) + st->l1.l1l2(st, pr, NULL); + + } +} + +static void hisax_b_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static inline void D_L2L1(struct hisax_d_if *d_if, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) d_if; + ifc->l2l1(ifc, pr, arg); +} + +static inline void B_L2L1(struct hisax_b_if *b_if, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) b_if; + ifc->l2l1(ifc, pr, arg); +} + +static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg) +{ + struct hisax_d_if *d_if = (struct hisax_d_if *) ifc; + struct IsdnCardState *cs = d_if->cs; + struct PStack *st; + struct sk_buff *skb; + + switch (pr) { + case PH_ACTIVATE | INDICATION: + set_bit(0, &d_if->ph_state); + hisax_sched_event(cs, D_L1STATECHANGE); + break; + case PH_DEACTIVATE | INDICATION: + clear_bit(0, &d_if->ph_state); + hisax_sched_event(cs, D_L1STATECHANGE); + break; + case PH_DATA | INDICATION: + skb_queue_tail(&cs->rq, arg); + hisax_sched_event(cs, D_RCVBUFREADY); + break; + case PH_DATA | CONFIRM: + skb = skb_dequeue(&cs->sq); + if (skb) { + D_L2L1(d_if, PH_DATA | REQUEST, skb); + break; + } + clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); + for (st = cs->stlist; st; st = st->next) { + if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) { + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + break; + } + } + break; + case PH_DATA_E | INDICATION: + skb_queue_tail(&d_if->erq, arg); + hisax_sched_event(cs, E_RCVBUFREADY); + break; + default: + printk("pr %#x\n", pr); + break; + } +} + +static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg) +{ + struct hisax_b_if *b_if = (struct hisax_b_if *) ifc; + struct BCState *bcs = b_if->bcs; + struct PStack *st = bcs->st; + struct sk_buff *skb; + + // FIXME use isdnl1? + switch (pr) { + case PH_ACTIVATE | INDICATION: + st->l1.l1l2(st, pr, NULL); + break; + case PH_DEACTIVATE | INDICATION: + st->l1.l1l2(st, pr, NULL); + bcs->hw.b_if = NULL; + break; + case PH_DATA | INDICATION: + skb_queue_tail(&bcs->rqueue, arg); + hisax_b_sched_event(bcs, B_RCVBUFREADY); + break; + case PH_DATA | CONFIRM: + bcs->tx_cnt -= (int) arg; + if (bcs->st->lli.l1writewakeup) + bcs->st->lli.l1writewakeup(bcs->st, (int) arg); + skb = skb_dequeue(&bcs->squeue); + if (skb) { + B_L2L1(b_if, PH_DATA | REQUEST, skb); + break; + } + clear_bit(BC_FLG_BUSY, &bcs->Flag); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) { + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } + break; + default: + printk("hisax_b_l1l2 pr %#x\n", pr); + break; + } +} + +static void hisax_d_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = st->l1.hardware; + struct hisax_d_if *hisax_d_if = cs->hw.hisax_d_if; + struct sk_buff *skb = arg; + + switch (pr) { + case PH_DATA | REQUEST: + case PH_PULL | INDICATION: + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + Logl2Frame(cs, skb, "PH_DATA_REQ", 0); + // FIXME lock? + if (!test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + D_L2L1(hisax_d_if, PH_DATA | REQUEST, skb); + else + skb_queue_tail(&cs->sq, skb); + break; + case PH_PULL | REQUEST: + if (!test_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + else + set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + default: + D_L2L1(hisax_d_if, pr, arg); + break; + } +} + +static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg) +{ + return 0; +} + +static void hisax_b_l2l1(struct PStack *st, int pr, void *arg) +{ + struct BCState *bcs = st->l1.bcs; + struct hisax_b_if *b_if = bcs->hw.b_if; + + switch (pr) { + case PH_ACTIVATE | REQUEST: + B_L2L1(b_if, pr, (void *) st->l1.mode); + break; + case PH_DATA | REQUEST: + case PH_PULL | INDICATION: + // FIXME lock? + if (!test_and_set_bit(BC_FLG_BUSY, &bcs->Flag)) { + B_L2L1(b_if, PH_DATA | REQUEST, arg); + } else { + skb_queue_tail(&bcs->squeue, arg); + } + break; + case PH_PULL | REQUEST: + if (!test_bit(BC_FLG_BUSY, &bcs->Flag)) + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + else + set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + default: + B_L2L1(b_if, pr, arg); + break; + } +} + +static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs) +{ + struct IsdnCardState *cs = st->l1.hardware; + struct hisax_d_if *hisax_d_if = cs->hw.hisax_d_if; + + bcs->channel = st->l1.bc; + + bcs->hw.b_if = hisax_d_if->b_if[st->l1.bc]; + hisax_d_if->b_if[st->l1.bc]->bcs = bcs; + + st->l1.bcs = bcs; + st->l2.l2l1 = hisax_b_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + return 0; +} + +static void hisax_bc_close(struct BCState *bcs) +{ + struct hisax_b_if *b_if = bcs->hw.b_if; + + if (b_if) + B_L2L1(b_if, PH_DEACTIVATE | REQUEST, NULL); +} + +static void EChannel_proc_rcv(struct hisax_d_if *d_if) +{ + struct IsdnCardState *cs = d_if->cs; + u_char *ptr; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&d_if->erq)) != NULL) { + if (cs->debug & DEB_DLOG_HEX) { + ptr = cs->dlog; + if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) { + *ptr++ = 'E'; + *ptr++ = 'C'; + *ptr++ = 'H'; + *ptr++ = 'O'; + *ptr++ = ':'; + ptr += QuickHex(ptr, skb->data, skb->len); + ptr--; + *ptr++ = '\n'; + *ptr = 0; + HiSax_putstatus(cs, NULL, cs->dlog); + } else + HiSax_putstatus(cs, "LogEcho: ", + "warning Frame too big (%d)", + skb->len); + } + dev_kfree_skb_any(skb); + } +} + +void HiSax_mod_dec_use_count(struct IsdnCardState *cs) +{ + struct module *mod; + + if (cs && cs->cardmsg == hisax_cardmsg) { + mod = cs->hw.hisax_d_if->owner; + if (mod) + __MOD_DEC_USE_COUNT(mod); + } else { + MOD_DEC_USE_COUNT; + } +} + +void HiSax_mod_inc_use_count(struct IsdnCardState *cs) +{ + struct module *mod; + + if (cs && cs->cardmsg == hisax_cardmsg) { + mod = cs->hw.hisax_d_if->owner; + if (mod) + // hope we do win the race... + try_inc_mod_count(mod); + } else { + MOD_INC_USE_COUNT; + } +} + #include <linux/pci.h> static struct pci_device_id hisax_pci_tbl[] __initdata = { #ifdef CONFIG_HISAX_FRITZPCI - {PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID}, + {PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID}, #endif #ifdef CONFIG_HISAX_DIEHLDIVA {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20, PCI_ANY_ID, PCI_ANY_ID}, diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index 53616888bbcd..62f1bf4ac7c7 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -1,4 +1,4 @@ -/* $Id: elsa_ser.c,v 2.10.6.2 2001/06/09 15:14:17 kai Exp $ +/* $Id: elsa_ser.c,v 2.10.6.3 2001/08/17 12:34:26 kai Exp $ * * stuff for the serial modem on ELSA cards * @@ -14,10 +14,6 @@ #define RS_ISR_PASS_LIMIT 256 #define BASE_BAUD ( 1843200 / 16 ) -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - //#define SERIAL_DEBUG_OPEN 1 //#define SERIAL_DEBUG_INTR 1 //#define SERIAL_DEBUG_FLOW 1 @@ -257,7 +253,7 @@ inline int write_modem(struct BCState *bcs) { int ret=0; struct IsdnCardState *cs = bcs->cs; - int count, len, fp, buflen; + int count, len, fp; long flags; if (!bcs->tx_skb) @@ -266,12 +262,14 @@ write_modem(struct BCState *bcs) { return 0; save_flags(flags); cli(); - buflen = MAX_MODEM_BUF - cs->hw.elsa.transcnt; - len = MIN(buflen, bcs->tx_skb->len); + len = bcs->tx_skb->len; + if (len > MAX_MODEM_BUF - cs->hw.elsa.transcnt) + len = MAX_MODEM_BUF - cs->hw.elsa.transcnt; fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp; fp &= (MAX_MODEM_BUF -1); - count = MIN(len, MAX_MODEM_BUF - fp); - if (count < len) { + count = len; + if (count > MAX_MODEM_BUF - fp) { + count = MAX_MODEM_BUF - fp; memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count); skb_pull(bcs->tx_skb, count); cs->hw.elsa.transcnt += count; @@ -472,8 +470,9 @@ modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) { } fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp; fp &= (MAX_MODEM_BUF -1); - count = MIN(len, MAX_MODEM_BUF - fp); - if (count < len) { + count = len; + if (count > MAX_MODEM_BUF - fp) { + count = MAX_MODEM_BUF - fp; memcpy(cs->hw.elsa.transbuf + fp, msg, count); cs->hw.elsa.transcnt += count; msg += count; diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index d1f385ecaf50..423306a057f5 100644 --- a/drivers/isdn/hisax/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -1,4 +1,4 @@ -/* $Id: fsm.c,v 1.14.6.2 2001/05/26 15:19:57 kai Exp $ +/* $Id: fsm.c,v 1.14.6.3 2001/08/23 19:44:23 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -15,6 +15,14 @@ #define FSM_TIMER_DEBUG 0 +EXPORT_SYMBOL(FsmNew); +EXPORT_SYMBOL(FsmFree); +EXPORT_SYMBOL(FsmEvent); +EXPORT_SYMBOL(FsmChangeState); +EXPORT_SYMBOL(FsmInitTimer); +EXPORT_SYMBOL(FsmDelTimer); +EXPORT_SYMBOL(FsmRestartTimer); + int __init FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount) { diff --git a/drivers/isdn/hisax/fsm.h b/drivers/isdn/hisax/fsm.h new file mode 100644 index 000000000000..3a77d1eabdd0 --- /dev/null +++ b/drivers/isdn/hisax/fsm.h @@ -0,0 +1,48 @@ +#ifndef __FSM_H__ +#define __FSM_H__ + +#include <linux/timer.h> + +struct FsmInst; + +typedef void (* FSMFNPTR)(struct FsmInst *, int, void *); + +struct Fsm { + FSMFNPTR *jumpmatrix; + int state_count, event_count; + char **strEvent, **strState; +}; + +struct FsmInst { + struct Fsm *fsm; + int state; + int debug; + void *userdata; + int userint; + void (*printdebug) (struct FsmInst *, char *, ...); +}; + +struct FsmNode { + int state, event; + void (*routine) (struct FsmInst *, int, void *); +}; + +struct FsmTimer { + struct FsmInst *fi; + struct timer_list tl; + int event; + void *arg; +}; + +int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount); +void FsmFree(struct Fsm *fsm); +int FsmEvent(struct FsmInst *fi, int event, void *arg); +void FsmChangeState(struct FsmInst *fi, int newstate); +void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft); +int FsmAddTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); +void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); +void FsmDelTimer(struct FsmTimer *ft, int where); + +#endif diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index 344902707b79..38251b34ded9 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,4 +1,4 @@ -/* $Id: hisax.h,v 2.52.6.7 2001/07/18 16:02:15 kai Exp $ +/* $Id: hisax.h,v 2.52.6.8 2001/08/23 19:44:23 kai Exp $ * * Basic declarations, defines and prototypes * @@ -455,7 +455,6 @@ struct amd7930_hw { struct tq_struct tq_xmt; }; - #define BC_FLG_INIT 1 #define BC_FLG_ACTIV 2 #define BC_FLG_BUSY 3 @@ -512,6 +511,7 @@ struct BCState { struct tiger_hw tiger; struct amd7930_hw amd7930; struct w6692B_hw w6692; + struct hisax_b_if *b_if; } hw; }; @@ -897,6 +897,7 @@ struct IsdnCardState { struct bkm_hw ax; struct gazel_hw gazel; struct w6692_hw w6692; + struct hisax_d_if *hisax_d_if; } hw; int myid; isdn_if iif; @@ -991,7 +992,8 @@ struct IsdnCardState { #define ISDN_CTYPE_HFC_SX 37 #define ISDN_CTYPE_NETJET_U 38 #define ISDN_CTYPE_HFC_SP_PCMCIA 39 -#define ISDN_CTYPE_COUNT 39 +#define ISDN_CTYPE_DYNAMIC 40 +#define ISDN_CTYPE_COUNT 40 #ifdef ISDN_CHIP_ISAC diff --git a/drivers/isdn/hisax/hisax_debug.h b/drivers/isdn/hisax/hisax_debug.h new file mode 100644 index 000000000000..0c94449ac562 --- /dev/null +++ b/drivers/isdn/hisax/hisax_debug.h @@ -0,0 +1,80 @@ +/* + * Common debugging macros for use with the hisax driver + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* + * How to use: + * + * Before including this file, you need to + * #define __debug_variable my_debug + * where my_debug is a variable in your code which + * determines the debug bitmask. + * + * If CONFIG_HISAX_DEBUG is not set, all macros evaluate to nothing + */ + +#ifndef __HISAX_DEBUG_H__ +#define __HISAX_DEBUG_H__ + +#ifdef CONFIG_HISAX_DEBUG + +#define DBG(level, format, arg...) do { \ +if (level & __debug_variable) \ +printk(KERN_DEBUG __FUNCTION__ ": " format "\n" , ## arg); \ +} while (0) + +#define DBG_PACKET(level,data,count) \ + if (level & __debug_variable) dump_packet(__FUNCTION__,data,count) + +#define DBG_SKB(level,skb) \ + if ((level & __debug_variable) && skb) dump_packet(__FUNCTION__,skb->data,skb->len) + + +static void __attribute__((unused)) +dump_packet(const char *name,const u_char *data,int pkt_len) +{ +#define DUMP_HDR_SIZE 20 +#define DUMP_TLR_SIZE 8 + if (pkt_len) { + int i,len1,len2; + + printk(KERN_DEBUG "%s: length=%d,data=",name,pkt_len); + + if (pkt_len > DUMP_HDR_SIZE+ DUMP_TLR_SIZE) { + len1 = DUMP_HDR_SIZE; + len2 = DUMP_TLR_SIZE; + } else { + len1 = pkt_len > DUMP_HDR_SIZE ? DUMP_HDR_SIZE : pkt_len; + len2 = 0; + } + for (i = 0; i < len1; ++i) { + printk ("%.2x", data[i]); + } + if (len2) { + printk (".."); + for (i = pkt_len-DUMP_TLR_SIZE; i < pkt_len; ++i) { + printk ("%.2x", data[i]); + } + } + printk ("\n"); + } +#undef DUMP_HDR_SIZE +#undef DUMP_TLR_SIZE +} + +#else + +#define DBG(level, format, arg...) do {} while (0) +#define DBG_PACKET(level,data,count) do {} while (0) +#define DBG_SKB(level,skb) do {} while (0) + +#endif + +#endif diff --git a/drivers/isdn/hisax/hisax_if.h b/drivers/isdn/hisax/hisax_if.h new file mode 100644 index 000000000000..49a856a84ec8 --- /dev/null +++ b/drivers/isdn/hisax/hisax_if.h @@ -0,0 +1,54 @@ +#ifndef __HISAX_IF_H__ +#define __HISAX_IF_H__ + +#include <linux/skbuff.h> + +#define REQUEST 0 +#define CONFIRM 1 +#define INDICATION 2 +#define RESPONSE 3 + +#define PH_ACTIVATE 0x0100 +#define PH_DEACTIVATE 0x0110 +#define PH_DATA 0x0120 +#define PH_PULL 0x0130 +#define PH_DATA_E 0x0140 + +#define L1_MODE_NULL 0 +#define L1_MODE_TRANS 1 +#define L1_MODE_HDLC 2 +#define L1_MODE_EXTRN 3 +#define L1_MODE_HDLC_56K 4 +#define L1_MODE_MODEM 7 +#define L1_MODE_V32 8 +#define L1_MODE_FAX 9 + +struct hisax_if { + void *priv; // private to driver + void (*l1l2)(struct hisax_if *, int pr, void *arg); + void (*l2l1)(struct hisax_if *, int pr, void *arg); +}; + +struct hisax_b_if { + struct hisax_if ifc; + + // private to hisax + struct BCState *bcs; +}; + +struct hisax_d_if { + struct hisax_if ifc; + + // private to hisax + struct module *owner; + struct IsdnCardState *cs; + struct hisax_b_if *b_if[2]; + struct sk_buff_head erq; + long ph_state; +}; + +int hisax_register(struct hisax_d_if *hisax_if, struct hisax_b_if *b_if[], + char *name, int protocol); +void hisax_unregister(struct hisax_d_if *hisax_if); + +#endif diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index 792f9567d679..0a4ba9193048 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -1,4 +1,4 @@ -/* $Id: isdnl1.c,v 2.41.6.3 2001/05/26 15:19:57 kai Exp $ +/* $Id: isdnl1.c,v 2.41.6.4 2001/08/23 19:44:23 kai Exp $ * * isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards * based on the teles driver from Jan den Ouden @@ -15,7 +15,7 @@ * */ -const char *l1_revision = "$Revision: 2.41.6.3 $"; +const char *l1_revision = "$Revision: 2.41.6.4 $"; #define __NO_VERSION__ #include <linux/init.h> @@ -895,7 +895,8 @@ setstack_HiSax(struct PStack *st, struct IsdnCardState *cs) setstack_manager(st); st->l1.stlistp = &(cs->stlist); st->l2.l2l1 = dch_l2l1; - cs->setstack_d(st, cs); + if (cs->setstack_d) + cs->setstack_d(st, cs); } void diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h index 435638de1e76..988dfe5751d1 100644 --- a/drivers/isdn/hisax/isdnl1.h +++ b/drivers/isdn/hisax/isdnl1.h @@ -1,4 +1,4 @@ -/* $Id: isdnl1.h,v 2.9.6.1 2001/02/16 16:43:27 kai Exp $ +/* $Id: isdnl1.h,v 2.9.6.2 2001/08/23 19:44:23 kai Exp $ * * Layer 1 defines * @@ -14,6 +14,7 @@ #define D_RX_MON1 5 #define D_TX_MON0 6 #define D_TX_MON1 7 +#define E_RCVBUFREADY 8 #define B_RCVBUFREADY 0 #define B_XMTBUFREADY 1 diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h new file mode 100644 index 000000000000..333de24cb157 --- /dev/null +++ b/drivers/isdn/hisax/st5481.h @@ -0,0 +1,533 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#ifndef _ST5481_H_ +#define _ST5481_H_ + +// USB IDs, the Product Id is in the range 0x4810-0x481F + +#define ST_VENDOR_ID 0x0483 +#define ST5481_PRODUCT_ID 0x4810 +#define ST5481_PRODUCT_ID_MASK 0xFFF0 + +// ST5481 endpoints when using alternative setting 3 (2B+D). +// To get the endpoint address, OR with 0x80 for IN endpoints. + +#define EP_CTRL 0x00U /* Control endpoint */ +#define EP_INT 0x01U /* Interrupt endpoint */ +#define EP_B1_OUT 0x02U /* B1 channel out */ +#define EP_B1_IN 0x03U /* B1 channel in */ +#define EP_B2_OUT 0x04U /* B2 channel out */ +#define EP_B2_IN 0x05U /* B2 channel in */ +#define EP_D_OUT 0x06U /* D channel out */ +#define EP_D_IN 0x07U /* D channel in */ + +// Number of isochronous packets. With 20 packets we get +// 50 interrupts/sec for each endpoint. + +#define NUM_ISO_PACKETS_D 20 +#define NUM_ISO_PACKETS_B 20 + +// Size of each isochronous packet. +// In outgoing direction we need to match ISDN data rates: +// D: 2 bytes / msec -> 16 kbit / s +// B: 16 bytes / msec -> 64 kbit / s +#define SIZE_ISO_PACKETS_D_IN 16 +#define SIZE_ISO_PACKETS_D_OUT 2 +#define SIZE_ISO_PACKETS_B_IN 32 +#define SIZE_ISO_PACKETS_B_OUT 8 + +// If we overrun/underrun, we send one packet with +/- 2 bytes +#define B_FLOW_ADJUST 2 + +// Registers that are written using vendor specific device request +// on endpoint 0. + +#define LBA 0x02 /* S loopback */ +#define SET_DEFAULT 0x06 /* Soft reset */ +#define LBB 0x1D /* S maintenance loopback */ +#define STT 0x1e /* S force transmission signals */ +#define SDA_MIN 0x20 /* SDA-sin minimal value */ +#define SDA_MAX 0x21 /* SDA-sin maximal value */ +#define SDELAY_VALUE 0x22 /* Delay between Tx and Rx clock */ +#define IN_D_COUNTER 0x36 /* D receive channel fifo counter */ +#define OUT_D_COUNTER 0x37 /* D transmit channel fifo counter */ +#define IN_B1_COUNTER 0x38 /* B1 receive channel fifo counter */ +#define OUT_B1_COUNTER 0x39 /* B1 transmit channel fifo counter */ +#define IN_B2_COUNTER 0x3a /* B2 receive channel fifo counter */ +#define OUT_B2_COUNTER 0x3b /* B2 transmit channel fifo counter */ +#define FFCTRL_IN_D 0x3C /* D receive channel fifo threshold low */ +#define FFCTRH_IN_D 0x3D /* D receive channel fifo threshold high */ +#define FFCTRL_OUT_D 0x3E /* D transmit channel fifo threshold low */ +#define FFCTRH_OUT_D 0x3F /* D transmit channel fifo threshold high */ +#define FFCTRL_IN_B1 0x40 /* B1 receive channel fifo threshold low */ +#define FFCTRH_IN_B1 0x41 /* B1 receive channel fifo threshold high */ +#define FFCTRL_OUT_B1 0x42 /* B1 transmit channel fifo threshold low */ +#define FFCTRH_OUT_B1 0x43 /* B1 transmit channel fifo threshold high */ +#define FFCTRL_IN_B2 0x44 /* B2 receive channel fifo threshold low */ +#define FFCTRH_IN_B2 0x45 /* B2 receive channel fifo threshold high */ +#define FFCTRL_OUT_B2 0x46 /* B2 transmit channel fifo threshold low */ +#define FFCTRH_OUT_B2 0x47 /* B2 transmit channel fifo threshold high */ +#define MPMSK 0x4A /* Multi purpose interrupt MASK register */ +#define FFMSK_D 0x4c /* D fifo interrupt MASK register */ +#define FFMSK_B1 0x4e /* B1 fifo interrupt MASK register */ +#define FFMSK_B2 0x50 /* B2 fifo interrupt MASK register */ +#define GPIO_DIR 0x52 /* GPIO pins direction registers */ +#define GPIO_OUT 0x53 /* GPIO pins output register */ +#define GPIO_IN 0x54 /* GPIO pins input register */ +#define TXCI 0x56 /* CI command to be transmitted */ + + +// Format of the interrupt packet received on endpoint 1: +// +// +--------+--------+--------+--------+--------+--------+ +// !MPINT !FFINT_D !FFINT_B1!FFINT_B2!CCIST !GPIO_INT! +// +--------+--------+--------+--------+--------+--------+ + +// Offsets in the interrupt packet + +#define MPINT 0 +#define FFINT_D 1 +#define FFINT_B1 2 +#define FFINT_B2 3 +#define CCIST 4 +#define GPIO_INT 5 +#define INT_PKT_SIZE 6 + +// MPINT +#define LSD_INT 0x80 /* S line activity detected */ +#define RXCI_INT 0x40 /* Indicate primitive arrived */ +#define DEN_INT 0x20 /* Signal enabling data out of D Tx fifo */ +#define DCOLL_INT 0x10 /* D channel collision */ +#define AMIVN_INT 0x04 /* AMI violation number reached 2 */ +#define INFOI_INT 0x04 /* INFOi changed */ +#define DRXON_INT 0x02 /* Reception channel active */ +#define GPCHG_INT 0x01 /* GPIO pin value changed */ + +// FFINT_x +#define IN_OVERRUN 0x80 /* In fifo overrun */ +#define OUT_UNDERRUN 0x40 /* Out fifo underrun */ +#define IN_UP 0x20 /* In fifo thresholdh up-crossed */ +#define IN_DOWN 0x10 /* In fifo thresholdl down-crossed */ +#define OUT_UP 0x08 /* Out fifo thresholdh up-crossed */ +#define OUT_DOWN 0x04 /* Out fifo thresholdl down-crossed */ +#define IN_COUNTER_ZEROED 0x02 /* In down-counter reached 0 */ +#define OUT_COUNTER_ZEROED 0x01 /* Out down-counter reached 0 */ + +#define ANY_REC_INT (IN_OVERRUN+IN_UP+IN_DOWN+IN_COUNTER_ZEROED) +#define ANY_XMIT_INT (OUT_UNDERRUN+OUT_UP+OUT_DOWN+OUT_COUNTER_ZEROED) + + +// Level 1 commands that are sent using the TXCI device request +#define ST5481_CMD_DR 0x0 /* Deactivation Request */ +#define ST5481_CMD_RES 0x1 /* state machine RESet */ +#define ST5481_CMD_TM1 0x2 /* Test Mode 1 */ +#define ST5481_CMD_TM2 0x3 /* Test Mode 2 */ +#define ST5481_CMD_PUP 0x7 /* Power UP */ +#define ST5481_CMD_AR8 0x8 /* Activation Request class 1 */ +#define ST5481_CMD_AR10 0x9 /* Activation Request class 2 */ +#define ST5481_CMD_ARL 0xA /* Activation Request Loopback */ +#define ST5481_CMD_PDN 0xF /* Power DoWn */ + +// Turn on/off the LEDs using the GPIO device request. +// To use the B LEDs, number_of_leds must be set to 4 +#define B1_LED 0x10U +#define B2_LED 0x20U +#define GREEN_LED 0x40U +#define RED_LED 0x80U + +// D channel out states +enum { + ST_DOUT_NONE, + + ST_DOUT_SHORT_INIT, + ST_DOUT_SHORT_WAIT_DEN, + + ST_DOUT_LONG_INIT, + ST_DOUT_LONG_WAIT_DEN, + ST_DOUT_NORMAL, + + ST_DOUT_WAIT_FOR_UNDERRUN, + ST_DOUT_WAIT_FOR_NOT_BUSY, + ST_DOUT_WAIT_FOR_STOP, + ST_DOUT_WAIT_FOR_RESET, +}; + +#define DOUT_STATE_COUNT (ST_DOUT_WAIT_FOR_RESET + 1) + +// D channel out events +enum { + EV_DOUT_START_XMIT, + EV_DOUT_COMPLETE, + EV_DOUT_DEN, + EV_DOUT_RESETED, + EV_DOUT_STOPPED, + EV_DOUT_COLL, + EV_DOUT_UNDERRUN, +}; + +#define DOUT_EVENT_COUNT (EV_DOUT_UNDERRUN + 1) + +// ---------------------------------------------------------------------- + +enum { + ST_L1_F3, + ST_L1_F4, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1_STATE_COUNT (ST_L1_F8+1) + +// The first 16 entries match the Level 1 indications that +// are found at offset 4 (CCIST) in the interrupt packet + +enum { + EV_IND_DP, // 0000 Deactivation Pending + EV_IND_1, // 0001 + EV_IND_2, // 0010 + EV_IND_3, // 0011 + EV_IND_RSY, // 0100 ReSYnchronizing + EV_IND_5, // 0101 + EV_IND_6, // 0110 + EV_IND_7, // 0111 + EV_IND_AP, // 1000 Activation Pending + EV_IND_9, // 1001 + EV_IND_10, // 1010 + EV_IND_11, // 1011 + EV_IND_AI8, // 1100 Activation Indication class 8 + EV_IND_AI10,// 1101 Activation Indication class 10 + EV_IND_AIL, // 1110 Activation Indication Loopback + EV_IND_DI, // 1111 Deactivation Indication + EV_PH_ACTIVATE_REQ, + EV_PH_DEACTIVATE_REQ, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +#define ERR(format, arg...) \ +printk(KERN_ERR __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) + +#define WARN(format, arg...) \ +printk(KERN_WARNING __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) + +#define INFO(format, arg...) \ +printk(KERN_INFO __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) + +#include "st5481_hdlc.h" +#include "fsm.h" +#include "hisax_if.h" +#include <linux/skbuff.h> + +/* ====================================================================== + * FIFO handling + */ + +/* Generic FIFO structure */ +struct fifo { + u_char r,w,count,size; + spinlock_t lock; +}; + +/* + * Init an FIFO + */ +static inline void fifo_init(struct fifo *fifo, int size) +{ + fifo->r = fifo->w = fifo->count = 0; + fifo->size = size; + spin_lock_init(&fifo->lock); +} + +/* + * Add an entry to the FIFO + */ +static inline int fifo_add(struct fifo *fifo) +{ + unsigned long flags; + int index; + + if (!fifo) { + return -1; + } + + spin_lock_irqsave(&fifo->lock, flags); + if (fifo->count == fifo->size) { + // FIFO full + index = -1; + } else { + // Return index where to get the next data to add to the FIFO + index = fifo->w++ & (fifo->size-1); + fifo->count++; + } + spin_unlock_irqrestore(&fifo->lock, flags); + return index; +} + +/* + * Remove an entry from the FIFO with the index returned. + */ +static inline int fifo_remove(struct fifo *fifo) +{ + unsigned long flags; + int index; + + if (!fifo) { + return -1; + } + + spin_lock_irqsave(&fifo->lock, flags); + if (!fifo->count) { + // FIFO empty + index = -1; + } else { + // Return index where to get the next data from the FIFO + index = fifo->r++ & (fifo->size-1); + fifo->count--; + } + spin_unlock_irqrestore(&fifo->lock, flags); + + return index; +} + +/* ====================================================================== + * control pipe + */ +typedef void (*ctrl_complete_t)(void *); + +typedef struct ctrl_msg { + devrequest dr; + ctrl_complete_t complete; + void *context; +} ctrl_msg; + +/* FIFO of ctrl messages waiting to be sent */ +#define MAX_EP0_MSG 16 +struct ctrl_msg_fifo { + struct fifo f; + struct ctrl_msg data[MAX_EP0_MSG]; +}; + +#define MAX_DFRAME_LEN_L1 300 +#define HSCX_BUFMAX 4096 + +struct st5481_ctrl { + struct ctrl_msg_fifo msg_fifo; + unsigned long busy; + struct urb *urb; +}; + +struct st5481_intr { + // struct evt_fifo evt_fifo; + struct urb *urb; +}; + +struct st5481_d_out { + struct hdlc_vars hdlc_state; + struct urb *urb[2]; /* double buffering */ + unsigned long busy; + struct sk_buff *tx_skb; + struct FsmInst fsm; +}; + +struct st5481_b_out { + struct hdlc_vars hdlc_state; + struct urb *urb[2]; /* double buffering */ + u_char flow_event; + u_long busy; + struct sk_buff *tx_skb; +}; + +struct st5481_in { + struct hdlc_vars hdlc_state; + struct urb *urb[2]; /* double buffering */ + int mode; + int bufsize; + unsigned int num_packets; + unsigned int packet_size; + unsigned char ep, counter; + unsigned char *rcvbuf; + struct st5481_adapter *adapter; + struct hisax_if *hisax_if; +}; + +int st5481_setup_in(struct st5481_in *in); +void st5481_release_in(struct st5481_in *in); +void st5481_in_mode(struct st5481_in *in, int mode); + +struct st5481_bcs { + struct hisax_b_if b_if; + struct st5481_adapter *adapter; + struct st5481_in b_in; + struct st5481_b_out b_out; + int channel; + int mode; +}; + +struct st5481_adapter { + struct list_head list; + int number_of_leds; + struct usb_device *usb_dev; + struct hisax_d_if hisax_d_if; + + struct st5481_ctrl ctrl; + struct st5481_intr intr; + struct st5481_in d_in; + struct st5481_d_out d_out; + + unsigned char leds; + unsigned int led_counter; + + unsigned long event; + + struct FsmInst l1m; + struct FsmTimer timer; + + struct st5481_bcs bcs[2]; +}; + +#define TIMER3_VALUE 7000 + +/* ====================================================================== + * + */ + +/* + * Submit an URB with error reporting. This is a macro so + * the __FUNCTION__ returns the caller function name. + */ +#define SUBMIT_URB(urb) \ +({ \ + int status; \ + if ((status = usb_submit_urb(urb)) < 0) { \ + WARN("usb_submit_urb failed,status=%d", status); \ + } \ + status; \ +}) + +/* + * USB double buffering, return the URB index (0 or 1). + */ +static inline int get_buf_nr(struct urb *urbs[], struct urb *urb) +{ + return (urbs[0]==urb ? 0 : 1); +} + +/* ---------------------------------------------------------------------- */ + +/* B Channel */ + +int st5481_setup_b(struct st5481_bcs *bcs); +void st5481_release_b(struct st5481_bcs *bcs); +void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg); + +/* D Channel */ + +int st5481_setup_d(struct st5481_adapter *adapter); +void st5481_release_d(struct st5481_adapter *adapter); +void st5481_b_l2l1(struct hisax_if *b_if, int pr, void *arg); +int st5481_d_init(void); +void st5481_d_exit(void); + +/* USB */ +void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command); +int st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev, + unsigned int pipe, int num_packets, + int packet_size, int buf_size, + usb_complete_t complete, void *context); +void st5481_release_isocpipes(struct urb* urb[2]); + +int st5481_isoc_flatten(struct urb *urb); +void st5481_usb_pipe_reset(struct st5481_adapter *adapter, + u_char pipe, ctrl_complete_t complete, void *context); +void st5481_usb_ctrl_msg(struct st5481_adapter *adapter, + u8 request, u8 requesttype, u16 value, u16 index, + ctrl_complete_t complete, void *context); +void st5481_usb_device_ctrl_msg(struct st5481_adapter *adapter, + u8 request, u16 value, + ctrl_complete_t complete, void *context); +int st5481_setup_usb(struct st5481_adapter *adapter); +void st5481_release_usb(struct st5481_adapter *adapter); +void st5481_start(struct st5481_adapter *adapter); +void st5481_stop(struct st5481_adapter *adapter); + +// ---------------------------------------------------------------------- +// debugging macros + +#define __debug_variable st5481_debug +#include "hisax_debug.h" + +#ifdef CONFIG_HISAX_DEBUG + +extern int st5481_debug; + +#define DBG_ISO_PACKET(level,urb) \ + if (level & __debug_variable) dump_iso_packet(__FUNCTION__,urb) + +static void __attribute__((unused)) +dump_iso_packet(const char *name,urb_t *urb) +{ + int i,j; + int len,ofs; + u_char *data; + + printk(KERN_DEBUG "%s: packets=%d,errors=%d\n", + name,urb->number_of_packets,urb->error_count); + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->pipe & USB_DIR_IN) { + len = urb->iso_frame_desc[i].actual_length; + } else { + len = urb->iso_frame_desc[i].length; + } + ofs = urb->iso_frame_desc[i].offset; + printk(KERN_DEBUG "len=%.2d,ofs=%.3d ",len,ofs); + if (len) { + data = urb->transfer_buffer+ofs; + for (j=0; j < len; j++) { + printk ("%.2x", data[j]); + } + } + printk("\n"); + } +} + +static inline const char *ST5481_CMD_string(int evt) +{ + static char s[16]; + + switch (evt) { + case ST5481_CMD_DR: return "DR"; + case ST5481_CMD_RES: return "RES"; + case ST5481_CMD_TM1: return "TM1"; + case ST5481_CMD_TM2: return "TM2"; + case ST5481_CMD_PUP: return "PUP"; + case ST5481_CMD_AR8: return "AR8"; + case ST5481_CMD_AR10: return "AR10"; + case ST5481_CMD_ARL: return "ARL"; + case ST5481_CMD_PDN: return "PDN"; + }; + + sprintf(s,"0x%x",evt); + return s; +} + +#else + +#define DBG_ISO_PACKET(level,urb) do {} while (0) + +#endif + + + +#endif diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c new file mode 100644 index 000000000000..17b2a21f878a --- /dev/null +++ b/drivers/isdn/hisax/st5481_b.c @@ -0,0 +1,367 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include "st5481.h" + +static inline void B_L1L2(struct st5481_bcs *bcs, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if; + + ifc->l1l2(ifc, pr, arg); +} + +/* + * Encode and transmit next frame. + */ +static void usb_b_out(struct st5481_bcs *bcs,int buf_nr) +{ + struct st5481_b_out *b_out = &bcs->b_out; + struct st5481_adapter *adapter = bcs->adapter; + struct urb *urb; + unsigned int packet_size,offset; + int len,buf_size,bytes_sent; + int i; + struct sk_buff *skb; + + if (test_and_set_bit(buf_nr, &b_out->busy)) { + DBG(4,"ep %d urb %d busy",(bcs->channel+1)*2,buf_nr); + return; + } + urb = b_out->urb[buf_nr]; + + // Adjust isoc buffer size according to flow state + if(b_out->flow_event & (OUT_DOWN | OUT_UNDERRUN)) { + buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST; + packet_size = SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST; + DBG(4,"B%d,adjust flow,add %d bytes",bcs->channel+1,B_FLOW_ADJUST); + } else if(b_out->flow_event & OUT_UP){ + buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT - B_FLOW_ADJUST; + packet_size = SIZE_ISO_PACKETS_B_OUT - B_FLOW_ADJUST; + DBG(4,"B%d,adjust flow,remove %d bytes",bcs->channel+1,B_FLOW_ADJUST); + } else { + buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT; + packet_size = 8; + } + b_out->flow_event = 0; + + len = 0; + while (len < buf_size) { + if ((skb = b_out->tx_skb)) { + DBG_SKB(0x100, skb); + DBG(4,"B%d,len=%d",bcs->channel+1,skb->len); + + if (bcs->mode == L1_MODE_TRANS) { + bytes_sent = buf_size - len; + if (skb->len < bytes_sent) + bytes_sent = skb->len; + + memcpy(urb->transfer_buffer+len, skb->data, bytes_sent); + + len += bytes_sent; + } else { + len += hdlc_encode(&b_out->hdlc_state, + skb->data, skb->len, &bytes_sent, + urb->transfer_buffer+len, buf_size-len); + } + + skb_pull(skb, bytes_sent); + + if (!skb->len) { + // Frame sent + b_out->tx_skb = NULL; + B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize); + dev_kfree_skb_any(skb); + +/* if (!(bcs->tx_skb = skb_dequeue(&bcs->sq))) { */ +/* st5481B_sched_event(bcs, B_XMTBUFREADY); */ +/* } */ + } + } else { + if (bcs->mode == L1_MODE_TRANS) { + memset(urb->transfer_buffer+len, 0xff, buf_size-len); + len = buf_size; + } else { + // Send flags + len += hdlc_encode(&b_out->hdlc_state, + NULL, 0, &bytes_sent, + urb->transfer_buffer+len, buf_size-len); + } + } + } + + // Prepare the URB + for (i = 0, offset = 0; offset < len; i++) { + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length = packet_size; + offset += packet_size; + packet_size = SIZE_ISO_PACKETS_B_OUT; + } + urb->transfer_buffer_length = len; + urb->number_of_packets = i; + urb->dev = adapter->usb_dev; + + DBG_ISO_PACKET(0x200,urb); + + SUBMIT_URB(urb); +} + +/* + * Start transfering (flags or data) on the B channel, since + * FIFO counters has been set to a non-zero value. + */ +static void st5481B_start_xfer(void *context) +{ + struct st5481_bcs *bcs = context; + + DBG(4,"B%d",bcs->channel+1); + + // Start transmitting (flags or data) on B channel + + usb_b_out(bcs,0); + usb_b_out(bcs,1); +} + +/* + * If the adapter has only 2 LEDs, the green + * LED will blink with a rate depending + * on the number of channels opened. + */ +static void led_blink(struct st5481_adapter *adapter) +{ + u_char leds = adapter->leds; + + // 50 frames/sec for each channel + if (++adapter->led_counter % 50) { + return; + } + + if (adapter->led_counter % 100) { + leds |= GREEN_LED; + } else { + leds &= ~GREEN_LED; + } + + st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, leds, NULL, NULL); +} + +static void usb_b_out_complete(struct urb *urb) +{ + struct st5481_bcs *bcs = urb->context; + struct st5481_b_out *b_out = &bcs->b_out; + struct st5481_adapter *adapter = bcs->adapter; + int buf_nr; + + buf_nr = get_buf_nr(b_out->urb, urb); + test_and_clear_bit(buf_nr, &b_out->busy); + + if (urb->status < 0) { + if (urb->status != USB_ST_URB_KILLED) { + WARN("urb status %d",urb->status); + if (b_out->busy == 0) { + st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL); + } + } else { + DBG(1,"urb killed"); + return; // Give up + } + } + + usb_b_out(bcs,buf_nr); + + if (adapter->number_of_leds == 2) + led_blink(adapter); +} + +/* + * Start or stop the transfer on the B channel. + */ +static void st5481B_mode(struct st5481_bcs *bcs, int mode) +{ + struct st5481_b_out *b_out = &bcs->b_out; + struct st5481_adapter *adapter = bcs->adapter; + + DBG(4,"B%d,mode=%d", bcs->channel + 1, mode); + + if (bcs->mode == mode) + return; + + bcs->mode = mode; + + // Cancel all USB transfers on this B channel + usb_unlink_urb(b_out->urb[0]); + usb_unlink_urb(b_out->urb[1]); + b_out->busy = 0; + + st5481_in_mode(&bcs->b_in, mode); + if (bcs->mode != L1_MODE_NULL) { + // Open the B channel + if (bcs->mode != L1_MODE_TRANS) { + hdlc_out_init(&b_out->hdlc_state, 0, bcs->mode == L1_MODE_HDLC_56K); + } + st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2, NULL, NULL); + + // Enable B channel interrupts + st5481_usb_device_ctrl_msg(adapter, FFMSK_B1+(bcs->channel*2), + OUT_UP+OUT_DOWN+OUT_UNDERRUN, NULL, NULL); + + // Enable B channel FIFOs + st5481_usb_device_ctrl_msg(adapter, OUT_B1_COUNTER+(bcs->channel*2), 32, st5481B_start_xfer, bcs); + if (adapter->number_of_leds == 4) { + if (bcs->channel == 0) { + adapter->leds |= B1_LED; + } else { + adapter->leds |= B2_LED; + } + } + } else { + // Disble B channel interrupts + st5481_usb_device_ctrl_msg(adapter, FFMSK_B1+(bcs->channel*2), 0, NULL, NULL); + + // Disable B channel FIFOs + st5481_usb_device_ctrl_msg(adapter, OUT_B1_COUNTER+(bcs->channel*2), 0, NULL, NULL); + + if (adapter->number_of_leds == 4) { + if (bcs->channel == 0) { + adapter->leds &= ~B1_LED; + } else { + adapter->leds &= ~B2_LED; + } + } else { + st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL); + } + if (b_out->tx_skb) { + dev_kfree_skb_any(b_out->tx_skb); + b_out->tx_skb = NULL; + } + + } +} + +static int __devinit st5481_setup_b_out(struct st5481_bcs *bcs) +{ + struct usb_device *dev = bcs->adapter->usb_dev; + struct usb_interface_descriptor *altsetting; + struct usb_endpoint_descriptor *endpoint; + struct st5481_b_out *b_out = &bcs->b_out; + + DBG(4,""); + + altsetting = &(dev->config->interface[0].altsetting[3]); + + // Allocate URBs and buffers for the B channel out + endpoint = &altsetting->endpoint[EP_B1_OUT - 1 + bcs->channel * 2]; + + DBG(4,"endpoint address=%02x,packet size=%d", + endpoint->bEndpointAddress,endpoint->wMaxPacketSize); + + // Allocate memory for 8000bytes/sec + extra bytes if underrun + return st5481_setup_isocpipes(b_out->urb, dev, + usb_sndisocpipe(dev, endpoint->bEndpointAddress), + NUM_ISO_PACKETS_B, SIZE_ISO_PACKETS_B_OUT, + NUM_ISO_PACKETS_B * SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST, + usb_b_out_complete, bcs); +} + +static void __devexit st5481_release_b_out(struct st5481_bcs *bcs) +{ + struct st5481_b_out *b_out = &bcs->b_out; + + DBG(4,""); + + st5481_release_isocpipes(b_out->urb); +} + +int __devinit st5481_setup_b(struct st5481_bcs *bcs) +{ + int retval; + + DBG(4,""); + + retval = st5481_setup_b_out(bcs); + if (retval) + goto err; + bcs->b_in.bufsize = HSCX_BUFMAX; + bcs->b_in.num_packets = NUM_ISO_PACKETS_B; + bcs->b_in.packet_size = SIZE_ISO_PACKETS_B_IN; + bcs->b_in.ep = (bcs->channel ? EP_B2_IN : EP_B1_IN) | USB_DIR_IN; + bcs->b_in.counter = bcs->channel ? IN_B2_COUNTER : IN_B1_COUNTER; + bcs->b_in.adapter = bcs->adapter; + bcs->b_in.hisax_if = &bcs->b_if.ifc; + retval = st5481_setup_in(&bcs->b_in); + if (retval) + goto err_b_out; + + + return 0; + + err_b_out: + st5481_release_b_out(bcs); + err: + return retval; +} + +/* + * Release buffers and URBs for the B channels + */ +void __devexit st5481_release_b(struct st5481_bcs *bcs) +{ + DBG(4,""); + + st5481_release_in(&bcs->b_in); + st5481_release_b_out(bcs); +} + +/* + * st5481_b_l2l1 is the entry point for upper layer routines that want to + * transmit on the B channel. PH_DATA | REQUEST is a normal packet that + * we either start transmitting (if idle) or queue (if busy). + * PH_PULL | REQUEST can be called to request a callback message + * (PH_PULL | CONFIRM) + * once the link is idle. After a "pull" callback, the upper layer + * routines can use PH_PULL | INDICATION to send data. + */ +void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg) +{ + struct st5481_bcs *bcs = ifc->priv; + struct sk_buff *skb = arg; + int mode; + + DBG(4, ""); + + switch (pr) { + case PH_DATA | REQUEST: + if (bcs->b_out.tx_skb) + BUG(); + + bcs->b_out.tx_skb = skb; + break; + case PH_ACTIVATE | REQUEST: + mode = (int) arg; + DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode); + st5481B_mode(bcs, mode); + B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL); + break; + case PH_DEACTIVATE | REQUEST: + DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1); + st5481B_mode(bcs, L1_MODE_NULL); + B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL); + break; + default: + WARN("pr %#x\n", pr); + } +} diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c new file mode 100644 index 000000000000..78c4e4961368 --- /dev/null +++ b/drivers/isdn/hisax/st5481_d.c @@ -0,0 +1,774 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include "st5481.h" + +static void ph_connect(struct st5481_adapter *adapter); +static void ph_disconnect(struct st5481_adapter *adapter); + +static struct Fsm l1fsm; + +static char *strL1State[] = +{ + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +static char *strL1Event[] = +{ + "EV_IND_DP", + "EV_IND_1", + "EV_IND_2", + "EV_IND_3", + "EV_IND_RSY", + "EV_IND_5", + "EV_IND_6", + "EV_IND_7", + "EV_IND_AP", + "EV_IND_9", + "EV_IND_10", + "EV_IND_11", + "EV_IND_AI8", + "EV_IND_AI10", + "EV_IND_AIL", + "EV_IND_DI", + "EV_PH_ACTIVATE_REQ", + "EV_PH_DEACTIVATE_REQ", + "EV_TIMER3", +}; + +static inline void D_L1L2(struct st5481_adapter *adapter, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) &adapter->hisax_d_if; + + ifc->l1l2(ifc, pr, arg); +} + +static void +l1_go_f3(struct FsmInst *fi, int event, void *arg) +{ + struct st5481_adapter *adapter = fi->userdata; + + if (fi->state == ST_L1_F7) + ph_disconnect(adapter); + + FsmChangeState(fi, ST_L1_F3); + D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL); +} + +static void +l1_go_f6(struct FsmInst *fi, int event, void *arg) +{ + struct st5481_adapter *adapter = fi->userdata; + + if (fi->state == ST_L1_F7) + ph_disconnect(adapter); + + FsmChangeState(fi, ST_L1_F6); +} + +static void +l1_go_f7(struct FsmInst *fi, int event, void *arg) +{ + struct st5481_adapter *adapter = fi->userdata; + + FsmDelTimer(&adapter->timer, 0); + ph_connect(adapter); + FsmChangeState(fi, ST_L1_F7); + D_L1L2(adapter, PH_ACTIVATE | INDICATION, NULL); +} + +static void +l1_go_f8(struct FsmInst *fi, int event, void *arg) +{ + struct st5481_adapter *adapter = fi->userdata; + + if (fi->state == ST_L1_F7) + ph_disconnect(adapter); + + FsmChangeState(fi, ST_L1_F8); +} + +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct st5481_adapter *adapter = fi->userdata; + + st5481_ph_command(adapter, ST5481_CMD_DR); + FsmChangeState(fi, ST_L1_F3); + D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL); +} + +static void +l1_ignore(struct FsmInst *fi, int event, void *arg) +{ +} + +static void +l1_activate(struct FsmInst *fi, int event, void *arg) +{ + struct st5481_adapter *adapter = fi->userdata; + + st5481_ph_command(adapter, ST5481_CMD_DR); + st5481_ph_command(adapter, ST5481_CMD_PUP); + FsmRestartTimer(&adapter->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + st5481_ph_command(adapter, ST5481_CMD_AR8); + FsmChangeState(fi, ST_L1_F4); +} + +static struct FsmNode L1FnList[] __initdata = +{ + {ST_L1_F3, EV_IND_DP, l1_ignore}, + {ST_L1_F3, EV_IND_AP, l1_go_f6}, + {ST_L1_F3, EV_IND_AI8, l1_go_f7}, + {ST_L1_F3, EV_IND_AI10, l1_go_f7}, + {ST_L1_F3, EV_PH_ACTIVATE_REQ, l1_activate}, + + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_IND_DP, l1_go_f3}, + {ST_L1_F4, EV_IND_AP, l1_go_f6}, + {ST_L1_F4, EV_IND_AI8, l1_go_f7}, + {ST_L1_F4, EV_IND_AI10, l1_go_f7}, + + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_IND_DP, l1_go_f3}, + {ST_L1_F6, EV_IND_AP, l1_ignore}, + {ST_L1_F6, EV_IND_AI8, l1_go_f7}, + {ST_L1_F6, EV_IND_AI10, l1_go_f7}, + {ST_L1_F7, EV_IND_RSY, l1_go_f8}, + + {ST_L1_F7, EV_IND_DP, l1_go_f3}, + {ST_L1_F7, EV_IND_AP, l1_go_f6}, + {ST_L1_F7, EV_IND_AI8, l1_ignore}, + {ST_L1_F7, EV_IND_AI10, l1_ignore}, + {ST_L1_F7, EV_IND_RSY, l1_go_f8}, + + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_IND_DP, l1_go_f3}, + {ST_L1_F8, EV_IND_AP, l1_go_f6}, + {ST_L1_F8, EV_IND_AI8, l1_go_f8}, + {ST_L1_F8, EV_IND_AI10, l1_go_f8}, + {ST_L1_F8, EV_IND_RSY, l1_ignore}, +}; + +static void l1m_debug(struct FsmInst *fi, char *fmt, ...) +{ + va_list args; + char buf[256]; + + va_start(args, fmt); + vsprintf(buf, fmt, args); + printk("buf %s\n", buf); + DBG(8, "%s", buf); + va_end(args); +} + +/* ====================================================================== + * D-Channel out + */ + +/* + D OUT state machine: + ==================== + + Transmit short frame (< 16 bytes of encoded data): + + L1 FRAME D_OUT_STATE USB D CHANNEL + -------- ----------- --- --------- + + FIXME + + -> [xx..xx] SHORT_INIT -> [7Exx..xxC1C27EFF] + SHORT_WAIT_DEN <> OUT_D_COUNTER=16 + + END_OF_SHORT <- DEN_EVENT -> 7Exx + xxxx + xxxx + xxxx + xxxx + xxxx + C1C1 + 7EFF + WAIT_FOR_RESET_IDLE <- D_UNDERRUN <- (8ms) + IDLE <> Reset pipe + + + + Transmit long frame (>= 16 bytes of encoded data): + + L1 FRAME D_OUT_STATE USB D CHANNEL + -------- ----------- --- --------- + + -> [xx...xx] IDLE + WAIT_FOR_STOP <> OUT_D_COUNTER=0 + WAIT_FOR_RESET <> Reset pipe + STOP + INIT_LONG_FRAME -> [7Exx..xx] + WAIT_DEN <> OUT_D_COUNTER=16 + OUT_NORMAL <- DEN_EVENT -> 7Exx + END_OF_FRAME_BUSY -> [xxxx] xxxx + END_OF_FRAME_NOT_BUSY -> [xxxx] xxxx + -> [xxxx] xxxx + -> [C1C2] xxxx + -> [7EFF] xxxx + xxxx + xxxx + .... + xxxx + C1C2 + 7EFF + <- D_UNDERRUN <- (> 8ms) + WAIT_FOR_STOP <> OUT_D_COUNTER=0 + WAIT_FOR_RESET <> Reset pipe + STOP + +*/ + +static struct Fsm dout_fsm; + +static char *strDoutState[] = +{ + "ST_DOUT_NONE", + + "ST_DOUT_SHORT_INIT", + "ST_DOUT_SHORT_WAIT_DEN", + + "ST_DOUT_LONG_INIT", + "ST_DOUT_LONG_WAIT_DEN", + "ST_DOUT_NORMAL", + + "ST_DOUT_WAIT_FOR_UNDERRUN", + "ST_DOUT_WAIT_FOR_NOT_BUSY", + "ST_DOUT_WAIT_FOR_STOP", + "ST_DOUT_WAIT_FOR_RESET", +}; + +static char *strDoutEvent[] = +{ + "EV_DOUT_START_XMIT", + "EV_DOUT_COMPLETE", + "EV_DOUT_DEN", + "EV_DOUT_RESETED", + "EV_DOUT_STOPPED", + "EV_DOUT_COLL", + "EV_DOUT_UNDERRUN", +}; + +static void dout_debug(struct FsmInst *fi, char *fmt, ...) +{ + va_list args; + char buf[256]; + + va_start(args, fmt); + vsprintf(buf, fmt, args); + DBG(0x2, "%s", buf); + va_end(args); +} + +static void dout_stop_event(void *context) +{ + struct st5481_adapter *adapter = context; + + FsmEvent(&adapter->d_out.fsm, EV_DOUT_STOPPED, NULL); +} + +/* + * Start the transfer of a D channel frame. + */ +static void usb_d_out(struct st5481_adapter *adapter, int buf_nr) +{ + struct st5481_d_out *d_out = &adapter->d_out; + struct urb *urb; + unsigned int num_packets, packet_offset; + int len, buf_size, bytes_sent; + struct sk_buff *skb; + iso_packet_descriptor_t *desc; + + if (d_out->fsm.state != ST_DOUT_NORMAL) + return; + + if (test_and_set_bit(buf_nr, &d_out->busy)) { + DBG(2, "ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy); + return; + } + urb = d_out->urb[buf_nr]; + + skb = d_out->tx_skb; + + buf_size = NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT; + + if (skb) { + len = hdlc_encode(&d_out->hdlc_state, + skb->data, skb->len, &bytes_sent, + urb->transfer_buffer, buf_size); + skb_pull(skb,bytes_sent); + } else { + // Send flags or idle + len = hdlc_encode(&d_out->hdlc_state, + NULL, 0, &bytes_sent, + urb->transfer_buffer, buf_size); + } + + if (len < buf_size) { + FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN); + } + if (skb && !skb->len) { + d_out->tx_skb = NULL; + D_L1L2(adapter, PH_DATA | CONFIRM, NULL); + dev_kfree_skb_any(skb); + } + + // Prepare the URB + urb->transfer_buffer_length = len; + num_packets = 0; + packet_offset = 0; + while (packet_offset < len) { + desc = &urb->iso_frame_desc[num_packets]; + desc->offset = packet_offset; + desc->length = SIZE_ISO_PACKETS_D_OUT; + if (len - packet_offset < desc->length) + desc->length = len - packet_offset; + num_packets++; + packet_offset += desc->length; + } + urb->number_of_packets = num_packets; + + // Prepare the URB + urb->dev = adapter->usb_dev; + // Need to transmit the next buffer 2ms after the DEN_EVENT + urb->transfer_flags = 0; + urb->start_frame = usb_get_current_frame_number(adapter->usb_dev)+2; + + DBG_ISO_PACKET(0x20,urb); + + if (usb_submit_urb(urb) < 0) { + // There is another URB queued up + urb->transfer_flags = USB_ISO_ASAP; + SUBMIT_URB(urb); + } +} + +static void fifo_reseted(void *context) +{ + struct st5481_adapter *adapter = context; + + FsmEvent(&adapter->d_out.fsm, EV_DOUT_RESETED, NULL); +} + +static void usb_d_out_complete(struct urb *urb) +{ + struct st5481_adapter *adapter = urb->context; + struct st5481_d_out *d_out = &adapter->d_out; + int buf_nr; + + DBG(2, ""); + + buf_nr = get_buf_nr(d_out->urb, urb); + test_and_clear_bit(buf_nr, &d_out->busy); + + if (urb->status < 0) { + if (urb->status != USB_ST_URB_KILLED) { + WARN("urb status %d",urb->status); + if (d_out->busy == 0) { + st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter); + } + return; + } else { + DBG(1,"urb killed"); + return; // Give up + } + } + + FsmEvent(&adapter->d_out.fsm, EV_DOUT_COMPLETE, (void *) buf_nr); +} + +/* ====================================================================== */ + +static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg) +{ + // FIXME unify? + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + struct urb *urb; + int len, bytes_sent; + struct sk_buff *skb; + int buf_nr = 0; + + skb = d_out->tx_skb; + + DBG(2,"len=%d",skb->len); + + hdlc_out_init(&d_out->hdlc_state, 1, 0); + + if (test_and_set_bit(buf_nr, &d_out->busy)) { + WARN("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy); + return; + } + urb = d_out->urb[buf_nr]; + + DBG_SKB(0x10, skb); + len = hdlc_encode(&d_out->hdlc_state, + skb->data, skb->len, &bytes_sent, + urb->transfer_buffer, 16); + skb_pull(skb, bytes_sent); + + if(len < 16) + FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_INIT); + else + FsmChangeState(&d_out->fsm, ST_DOUT_LONG_INIT); + + if (skb->len == 0) { + d_out->tx_skb = NULL; + D_L1L2(adapter, PH_DATA | CONFIRM, NULL); + dev_kfree_skb_any(skb); + } + +// Prepare the URB + urb->transfer_buffer_length = len; + + urb->iso_frame_desc[0].offset = 0; + urb->iso_frame_desc[0].length = len; + urb->number_of_packets = 1; + + // Prepare the URB + urb->dev = adapter->usb_dev; + urb->transfer_flags = USB_ISO_ASAP; + + DBG_ISO_PACKET(0x20,urb); + SUBMIT_URB(urb); +} + +static void dout_short_fifo(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_WAIT_DEN); + st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL); +} + +static void dout_end_short_frame(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN); +} + +static void dout_long_enable_fifo(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL); + FsmChangeState(&d_out->fsm, ST_DOUT_LONG_WAIT_DEN); +} + +static void dout_long_den(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + FsmChangeState(&d_out->fsm, ST_DOUT_NORMAL); + usb_d_out(adapter, 0); + usb_d_out(adapter, 1); +} + +static void dout_reset(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_RESET); + st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter); +} + +static void dout_stop(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_STOP); + st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 0, dout_stop_event, adapter); +} + +static void dout_underrun(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + if (test_bit(0, &d_out->busy) || test_bit(1, &d_out->busy)) { + FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_NOT_BUSY); + } else { + dout_stop(fsm, event, arg); + } +} + +static void dout_check_busy(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + if (!test_bit(0, &d_out->busy) && !test_bit(1, &d_out->busy)) + dout_stop(fsm, event, arg); +} + +static void dout_reseted(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + struct st5481_d_out *d_out = &adapter->d_out; + + FsmChangeState(&d_out->fsm, ST_DOUT_NONE); + // FIXME locking + if (d_out->tx_skb) + FsmEvent(&d_out->fsm, EV_DOUT_START_XMIT, NULL); +} + +static void dout_complete(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + int buf_nr = (int) arg; + + usb_d_out(adapter, buf_nr); +} + +static void dout_ignore(struct FsmInst *fsm, int event, void *arg) +{ +} + +static struct FsmNode DoutFnList[] __initdata = +{ + {ST_DOUT_NONE, EV_DOUT_START_XMIT, dout_start_xmit}, + + {ST_DOUT_SHORT_INIT, EV_DOUT_COMPLETE, dout_short_fifo}, + + {ST_DOUT_SHORT_WAIT_DEN, EV_DOUT_DEN, dout_end_short_frame}, + {ST_DOUT_SHORT_WAIT_DEN, EV_DOUT_UNDERRUN, dout_underrun}, + + {ST_DOUT_LONG_INIT, EV_DOUT_COMPLETE, dout_long_enable_fifo}, + + {ST_DOUT_LONG_WAIT_DEN, EV_DOUT_DEN, dout_long_den}, + {ST_DOUT_LONG_WAIT_DEN, EV_DOUT_UNDERRUN, dout_underrun}, + + {ST_DOUT_NORMAL, EV_DOUT_UNDERRUN, dout_underrun}, + {ST_DOUT_NORMAL, EV_DOUT_COMPLETE, dout_complete}, + + {ST_DOUT_WAIT_FOR_UNDERRUN, EV_DOUT_UNDERRUN, dout_underrun}, + {ST_DOUT_WAIT_FOR_UNDERRUN, EV_DOUT_COMPLETE, dout_ignore}, + + {ST_DOUT_WAIT_FOR_NOT_BUSY, EV_DOUT_COMPLETE, dout_check_busy}, + + {ST_DOUT_WAIT_FOR_STOP, EV_DOUT_STOPPED, dout_reset}, + + {ST_DOUT_WAIT_FOR_RESET, EV_DOUT_RESETED, dout_reseted}, +}; + +void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg) +{ + struct st5481_adapter *adapter = hisax_d_if->priv; + struct sk_buff *skb = arg; + + switch (pr) { + case PH_ACTIVATE | REQUEST: + FsmEvent(&adapter->l1m, EV_PH_ACTIVATE_REQ, NULL); + break; + case PH_DEACTIVATE | REQUEST: + FsmEvent(&adapter->l1m, EV_PH_DEACTIVATE_REQ, NULL); + break; + case PH_DATA | REQUEST: + DBG(2, "PH_DATA REQUEST len %d", skb->len); + if (adapter->d_out.tx_skb) + BUG(); + + adapter->d_out.tx_skb = skb; + FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL); + break; + default: + WARN("pr %#x\n", pr); + break; + } +} + +/* ====================================================================== + */ + +/* + * Start receiving on the D channel since entered state F7. + */ +static void ph_connect(struct st5481_adapter *adapter) +{ + struct st5481_d_out *d_out = &adapter->d_out; + struct st5481_in *d_in = &adapter->d_in; + + DBG(8,""); + + FsmChangeState(&d_out->fsm, ST_DOUT_NONE); + + // st5481_usb_device_ctrl_msg(adapter, FFMSK_D, OUT_UNDERRUN, NULL, NULL); + st5481_usb_device_ctrl_msg(adapter, FFMSK_D, 0xfc, NULL, NULL); + st5481_in_mode(d_in, L1_MODE_HDLC); + +#if LOOPBACK + // Turn loopback on (data sent on B and D looped back) + st5481_usb_device_ctrl_msg(cs, LBB, 0x04, NULL, NULL); +#endif + + st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, NULL, NULL); + + // Turn on the green LED to tell that we are in state F7 + adapter->leds |= GREEN_LED; + st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL); +} + +/* + * Stop receiving on the D channel since not in state F7. + */ +static void ph_disconnect(struct st5481_adapter *adapter) +{ + DBG(8,""); + + st5481_in_mode(&adapter->d_in, L1_MODE_NULL); + + // Turn off the green LED to tell that we left state F7 + adapter->leds &= ~GREEN_LED; + st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL); +} + +static int __devinit st5481_setup_d_out(struct st5481_adapter *adapter) +{ + struct usb_device *dev = adapter->usb_dev; + struct usb_interface_descriptor *altsetting; + struct usb_endpoint_descriptor *endpoint; + struct st5481_d_out *d_out = &adapter->d_out; + + DBG(2,""); + + altsetting = &(dev->config->interface[0].altsetting[3]); + + // Allocate URBs and buffers for the D channel out + endpoint = &altsetting->endpoint[EP_D_OUT-1]; + + DBG(2,"endpoint address=%02x,packet size=%d", + endpoint->bEndpointAddress,endpoint->wMaxPacketSize); + + return st5481_setup_isocpipes(d_out->urb, dev, + usb_sndisocpipe(dev, endpoint->bEndpointAddress), + NUM_ISO_PACKETS_D, SIZE_ISO_PACKETS_D_OUT, + NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT, + usb_d_out_complete, adapter); +} + +static void __devexit st5481_release_d_out(struct st5481_adapter *adapter) +{ + struct st5481_d_out *d_out = &adapter->d_out; + + DBG(2,""); + + st5481_release_isocpipes(d_out->urb); +} + +int __devinit st5481_setup_d(struct st5481_adapter *adapter) +{ + int retval; + + DBG(2,""); + + retval = st5481_setup_d_out(adapter); + if (retval) + goto err; + adapter->d_in.bufsize = MAX_DFRAME_LEN_L1; + adapter->d_in.num_packets = NUM_ISO_PACKETS_D; + adapter->d_in.packet_size = SIZE_ISO_PACKETS_D_IN; + adapter->d_in.ep = EP_D_IN | USB_DIR_IN; + adapter->d_in.counter = IN_D_COUNTER; + adapter->d_in.adapter = adapter; + adapter->d_in.hisax_if = &adapter->hisax_d_if.ifc; + retval = st5481_setup_in(&adapter->d_in); + if (retval) + goto err_d_out; + + adapter->l1m.fsm = &l1fsm; + adapter->l1m.state = ST_L1_F3; + adapter->l1m.debug = 1; + adapter->l1m.userdata = adapter; + adapter->l1m.printdebug = l1m_debug; + FsmInitTimer(&adapter->l1m, &adapter->timer); + + adapter->d_out.fsm.fsm = &dout_fsm; + adapter->d_out.fsm.state = ST_DOUT_NONE; + adapter->d_out.fsm.debug = 1; + adapter->d_out.fsm.userdata = adapter; + adapter->d_out.fsm.printdebug = dout_debug; + + return 0; + + err_d_out: + st5481_release_d_out(adapter); + err: + return retval; +} + +void __devexit st5481_release_d(struct st5481_adapter *adapter) +{ + DBG(2,""); + + st5481_release_in(&adapter->d_in); + st5481_release_d_out(adapter); +} + +/* ====================================================================== + * init / exit + */ + +int __init st5481_d_init(void) +{ + int retval; + + l1fsm.state_count = L1_STATE_COUNT; + l1fsm.event_count = L1_EVENT_COUNT; + l1fsm.strEvent = strL1Event; + l1fsm.strState = strL1State; + retval = FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList)); + if (retval) + goto err; + + dout_fsm.state_count = DOUT_STATE_COUNT; + dout_fsm.event_count = DOUT_EVENT_COUNT; + dout_fsm.strEvent = strDoutEvent; + dout_fsm.strState = strDoutState; + retval = FsmNew(&dout_fsm, DoutFnList, ARRAY_SIZE(DoutFnList)); + if (retval) + goto err_l1; + + return 0; + + err_l1: + FsmFree(&l1fsm); + err: + return retval; +} + +// can't be __exit +void st5481_d_exit(void) +{ + FsmFree(&l1fsm); + FsmFree(&dout_fsm); +} diff --git a/drivers/isdn/hisax/st5481_hdlc.c b/drivers/isdn/hisax/st5481_hdlc.c new file mode 100644 index 000000000000..2ea5136bed49 --- /dev/null +++ b/drivers/isdn/hisax/st5481_hdlc.c @@ -0,0 +1,620 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include "st5481_hdlc.h" + +static const unsigned short int crc16_tab[] = { + 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf, + 0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7, + 0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e, + 0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876, + 0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd, + 0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5, + 0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c, + 0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974, + 0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb, + 0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3, + 0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a, + 0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72, + 0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9, + 0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1, + 0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738, + 0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70, + 0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7, + 0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff, + 0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036, + 0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e, + 0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5, + 0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd, + 0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134, + 0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c, + 0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3, + 0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb, + 0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232, + 0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a, + 0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1, + 0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9, + 0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330, + 0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78 +}; + + + +enum { + HDLC_FAST_IDLE,HDLC_GET_FLAG_B0,HDLC_GETFLAG_B1A6,HDLC_GETFLAG_B7, + HDLC_GET_DATA,HDLC_FAST_FLAG +}; + +enum { + HDLC_SEND_DATA,HDLC_SEND_CRC1,HDLC_SEND_FAST_FLAG, + HDLC_SEND_FIRST_FLAG,HDLC_SEND_CRC2,HDLC_SEND_CLOSING_FLAG, + HDLC_SEND_IDLE1,HDLC_SEND_FAST_IDLE,HDLC_SENDFLAG_B0, + HDLC_SENDFLAG_B1A6,HDLC_SENDFLAG_B7,STOPPED +}; + +void +hdlc_rcv_init(struct hdlc_vars *hdlc, int do_adapt56) +{ + hdlc->bit_shift = 0; + hdlc->hdlc_bits1 = 0; + hdlc->data_bits = 0; + hdlc->ffbit_shift = 0; + hdlc->data_received = 0; + hdlc->state = HDLC_GET_DATA; + hdlc->do_adapt56 = do_adapt56; + hdlc->dchannel = 0; + hdlc->crc = 0; + hdlc->cbin = 0; + hdlc->shift_reg = 0; + hdlc->ffvalue = 0; + hdlc->dstpos = 0; +} + +void +hdlc_out_init(struct hdlc_vars *hdlc, int is_d_channel, int do_adapt56) +{ + hdlc->bit_shift = 0; + hdlc->hdlc_bits1 = 0; + hdlc->data_bits = 0; + hdlc->ffbit_shift = 0; + hdlc->data_received = 0; + hdlc->do_closing = 0; + hdlc->ffvalue = 0; + if (is_d_channel) { + hdlc->dchannel = 1; + hdlc->state = HDLC_SEND_FIRST_FLAG; + } else { + hdlc->dchannel = 0; + hdlc->state = HDLC_SEND_FAST_FLAG; + hdlc->ffvalue = 0x7e; + } + hdlc->cbin = 0x7e; + hdlc->bit_shift = 0; + if(do_adapt56){ + hdlc->do_adapt56 = 1; + hdlc->data_bits = 0; + hdlc->state = HDLC_SENDFLAG_B0; + } else { + hdlc->do_adapt56 = 0; + hdlc->data_bits = 8; + } + hdlc->shift_reg = 0; +} + +/* + hdlc_decode - decodes HDLC frames from a transparent bit stream. + + The source buffer is scanned for valid HDLC frames looking for + flags (01111110) to indicate the start of a frame. If the start of + the frame is found, the bit stuffing is removed (0 after 5 1's). + When a new flag is found, the complete frame has been received + and the CRC is checked. + If a valid frame is found, the function returns the frame length + excluding the CRC with the bit HDLC_END_OF_FRAME set. + If the beginning of a valid frame is found, the function returns + the length. + If a framing error is found (too many 1s and not a flag) the function + returns the length with the bit HDLC_FRAMING_ERROR set. + If a CRC error is found the function returns the length with the + bit HDLC_CRC_ERROR set. + If the frame length exceeds the destination buffer size, the function + returns the length with the bit HDLC_LENGTH_ERROR set. + + src - source buffer + slen - source buffer length + count - number of bytes removed (decoded) from the source buffer + dst _ destination buffer + dsize - destination buffer size + returns - number of decoded bytes in the destination buffer and status + flag. + */ +int hdlc_decode(struct hdlc_vars *hdlc, const unsigned char *src, + int slen, int *count, unsigned char *dst, int dsize) +{ + int status=0; + + static const unsigned char fast_flag[]={ + 0x00,0x00,0x00,0x20,0x30,0x38,0x3c,0x3e,0x3f + }; + + static const unsigned char fast_flag_value[]={ + 0x00,0x7e,0xfc,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f + }; + + static const unsigned char fast_abort[]={ + 0x00,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff + }; + + *count = slen; + + while(slen > 0){ + if(hdlc->bit_shift==0){ + hdlc->cbin = *src++; + slen--; + hdlc->bit_shift = 8; + if(hdlc->do_adapt56){ + hdlc->bit_shift --; + } + } + + switch(hdlc->state){ + case STOPPED: + return 0; + case HDLC_FAST_IDLE: + if(hdlc->cbin == 0xff){ + hdlc->bit_shift = 0; + break; + } + hdlc->state = HDLC_GET_FLAG_B0; + hdlc->hdlc_bits1 = 0; + hdlc->bit_shift = 8; + break; + case HDLC_GET_FLAG_B0: + if(!(hdlc->cbin & 0x80)) { + hdlc->state = HDLC_GETFLAG_B1A6; + hdlc->hdlc_bits1 = 0; + } else { + if(!hdlc->do_adapt56){ + if(++hdlc->hdlc_bits1 >=8 ) if(hdlc->bit_shift==1) + hdlc->state = HDLC_FAST_IDLE; + } + } + hdlc->cbin<<=1; + hdlc->bit_shift --; + break; + case HDLC_GETFLAG_B1A6: + if(hdlc->cbin & 0x80){ + hdlc->hdlc_bits1++; + if(hdlc->hdlc_bits1==6){ + hdlc->state = HDLC_GETFLAG_B7; + } + } else { + hdlc->hdlc_bits1 = 0; + } + hdlc->cbin<<=1; + hdlc->bit_shift --; + break; + case HDLC_GETFLAG_B7: + if(hdlc->cbin & 0x80) { + hdlc->state = HDLC_GET_FLAG_B0; + } else { + hdlc->state = HDLC_GET_DATA; + hdlc->crc = 0xffff; + hdlc->shift_reg = 0; + hdlc->hdlc_bits1 = 0; + hdlc->data_bits = 0; + hdlc->data_received = 0; + } + hdlc->cbin<<=1; + hdlc->bit_shift --; + break; + case HDLC_GET_DATA: + if(hdlc->cbin & 0x80){ + hdlc->hdlc_bits1++; + switch(hdlc->hdlc_bits1){ + case 6: + break; + case 7: + if(hdlc->data_received) { + // bad frame + status = -HDLC_FRAMING_ERROR; + } + if(!hdlc->do_adapt56){ + if(hdlc->cbin==fast_abort[hdlc->bit_shift+1]){ + hdlc->state = HDLC_FAST_IDLE; + hdlc->bit_shift=1; + break; + } + } else { + hdlc->state = HDLC_GET_FLAG_B0; + } + break; + default: + hdlc->shift_reg>>=1; + hdlc->shift_reg |= 0x80; + hdlc->data_bits++; + break; + } + } else { + switch(hdlc->hdlc_bits1){ + case 5: + break; + case 6: + if(hdlc->data_received){ + if (hdlc->dstpos < 2) { + status = -HDLC_FRAMING_ERROR; + } else if (hdlc->crc != 0xf0b8){ + // crc error + status = -HDLC_CRC_ERROR; + } else { + // remove CRC + hdlc->dstpos -= 2; + // good frame + status = hdlc->dstpos; + } + } + hdlc->crc = 0xffff; + hdlc->shift_reg = 0; + hdlc->data_bits = 0; + if(!hdlc->do_adapt56){ + if(hdlc->cbin==fast_flag[hdlc->bit_shift]){ + hdlc->ffvalue = fast_flag_value[hdlc->bit_shift]; + hdlc->state = HDLC_FAST_FLAG; + hdlc->ffbit_shift = hdlc->bit_shift; + hdlc->bit_shift = 1; + } else { + hdlc->state = HDLC_GET_DATA; + hdlc->data_received = 0; + } + } else { + hdlc->state = HDLC_GET_DATA; + hdlc->data_received = 0; + } + break; + default: + hdlc->shift_reg>>=1; + hdlc->data_bits++; + break; + } + hdlc->hdlc_bits1 = 0; + } + if (status) { + hdlc->dstpos = 0; + *count -= slen; + hdlc->cbin <<= 1; + hdlc->bit_shift--; + return status; + } + if(hdlc->data_bits==8){ + unsigned cval; + + hdlc->data_bits = 0; + hdlc->data_received = 1; + cval = (hdlc->crc^hdlc->shift_reg) & 0xff; + hdlc->crc = (hdlc->crc>>8)^crc16_tab[cval]; + // good byte received + if (dsize--) { + dst[hdlc->dstpos++] = hdlc->shift_reg; + } else { + // frame too long + status = -HDLC_LENGTH_ERROR; + hdlc->dstpos = 0; + } + } + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_FAST_FLAG: + if(hdlc->cbin==hdlc->ffvalue){ + hdlc->bit_shift = 0; + break; + } else { + if(hdlc->cbin == 0xff){ + hdlc->state = HDLC_FAST_IDLE; + hdlc->bit_shift=0; + } else if(hdlc->ffbit_shift==8){ + hdlc->state = HDLC_GETFLAG_B7; + break; + } else { + hdlc->shift_reg = fast_abort[hdlc->ffbit_shift-1]; + hdlc->hdlc_bits1 = hdlc->ffbit_shift-2; + if(hdlc->hdlc_bits1<0)hdlc->hdlc_bits1 = 0; + hdlc->data_bits = hdlc->ffbit_shift-1; + hdlc->state = HDLC_GET_DATA; + hdlc->data_received = 0; + } + } + break; + default: + break; + } + } + *count -= slen; + return 0; +} + +/* + hdlc_encode - encodes HDLC frames to a transparent bit stream. + + The bit stream starts with a beginning flag (01111110). After + that each byte is added to the bit stream with bit stuffing added + (0 after 5 1's). + When the last byte has been removed from the source buffer, the + CRC (2 bytes is added) and the frame terminates with the ending flag. + For the dchannel, the idle character (all 1's) is also added at the end. + If this function is called with empty source buffer (slen=0), flags or + idle character will be generated. + + src - source buffer + slen - source buffer length + count - number of bytes removed (encoded) from source buffer + dst _ destination buffer + dsize - destination buffer size + returns - number of encoded bytes in the destination buffer +*/ +int hdlc_encode(struct hdlc_vars *hdlc, const unsigned char *src, + unsigned short slen, int *count, + unsigned char *dst, int dsize) +{ + static const unsigned char xfast_flag_value[] = { + 0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e + }; + + int len = 0; + + *count = slen; + + while (dsize > 0) { + if(hdlc->bit_shift==0){ + if(slen && !hdlc->do_closing){ + hdlc->shift_reg = *src++; + slen--; + if (slen == 0) + hdlc->do_closing = 1; /* closing sequence, CRC + flag(s) */ + hdlc->bit_shift = 8; + } else { + if(hdlc->state == HDLC_SEND_DATA){ + if(hdlc->data_received){ + hdlc->state = HDLC_SEND_CRC1; + hdlc->crc ^= 0xffff; + hdlc->bit_shift = 8; + hdlc->shift_reg = hdlc->crc & 0xff; + } else if(!hdlc->do_adapt56){ + hdlc->state = HDLC_SEND_FAST_FLAG; + } else { + hdlc->state = HDLC_SENDFLAG_B0; + } + } + + } + } + + switch(hdlc->state){ + case STOPPED: + while (dsize--) + *dst++ = 0xff; + + return dsize; + case HDLC_SEND_FAST_FLAG: + hdlc->do_closing = 0; + if(slen == 0){ + *dst++ = hdlc->ffvalue; + len++; + dsize--; + break; + } + if(hdlc->bit_shift==8){ + hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits); + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + hdlc->data_received = 1; + } + break; + case HDLC_SENDFLAG_B0: + hdlc->do_closing = 0; + hdlc->cbin <<= 1; + hdlc->data_bits++; + hdlc->hdlc_bits1 = 0; + hdlc->state = HDLC_SENDFLAG_B1A6; + break; + case HDLC_SENDFLAG_B1A6: + hdlc->cbin <<= 1; + hdlc->data_bits++; + hdlc->cbin++; + if(++hdlc->hdlc_bits1 == 6) + hdlc->state = HDLC_SENDFLAG_B7; + break; + case HDLC_SENDFLAG_B7: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(slen == 0){ + hdlc->state = HDLC_SENDFLAG_B0; + break; + } + if(hdlc->bit_shift==8){ + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + hdlc->data_received = 1; + } + break; + case HDLC_SEND_FIRST_FLAG: + hdlc->data_received = 1; + if(hdlc->data_bits==8){ + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + break; + } + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->shift_reg & 0x01) + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + if(hdlc->bit_shift==0){ + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + } + break; + case HDLC_SEND_DATA: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->hdlc_bits1 == 5){ + hdlc->hdlc_bits1 = 0; + break; + } + if(hdlc->bit_shift==8){ + unsigned cval; + + cval = (hdlc->crc^hdlc->shift_reg) & 0xff; + hdlc->crc = (hdlc->crc>>8)^crc16_tab[cval]; + } + if(hdlc->shift_reg & 0x01){ + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + break; + case HDLC_SEND_CRC1: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->hdlc_bits1 == 5){ + hdlc->hdlc_bits1 = 0; + break; + } + if(hdlc->shift_reg & 0x01){ + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + if(hdlc->bit_shift==0){ + hdlc->shift_reg = (hdlc->crc >> 8); + hdlc->state = HDLC_SEND_CRC2; + hdlc->bit_shift = 8; + } + break; + case HDLC_SEND_CRC2: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->hdlc_bits1 == 5){ + hdlc->hdlc_bits1 = 0; + break; + } + if(hdlc->shift_reg & 0x01){ + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + if(hdlc->bit_shift==0){ + hdlc->shift_reg = 0x7e; + hdlc->state = HDLC_SEND_CLOSING_FLAG; + hdlc->bit_shift = 8; + } + break; + case HDLC_SEND_CLOSING_FLAG: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->hdlc_bits1 == 5){ + hdlc->hdlc_bits1 = 0; + break; + } + if(hdlc->shift_reg & 0x01){ + hdlc->cbin++; + } + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + if(hdlc->bit_shift==0){ + hdlc->ffvalue = xfast_flag_value[hdlc->data_bits]; + if(hdlc->dchannel){ + hdlc->ffvalue = 0x7e; + hdlc->state = HDLC_SEND_IDLE1; + hdlc->bit_shift = 8-hdlc->data_bits; + if(hdlc->bit_shift==0) + hdlc->state = HDLC_SEND_FAST_IDLE; + } else { + if(!hdlc->do_adapt56){ + hdlc->state = HDLC_SEND_FAST_FLAG; + hdlc->data_received = 0; + } else { + hdlc->state = HDLC_SENDFLAG_B0; + hdlc->data_received = 0; + } + // Finished with this frame, send flags + if (dsize > 1) dsize = 1; + } + } + break; + case HDLC_SEND_IDLE1: + hdlc->do_closing = 0; + hdlc->cbin <<= 1; + hdlc->cbin++; + hdlc->data_bits++; + hdlc->bit_shift--; + if(hdlc->bit_shift==0){ + hdlc->state = HDLC_SEND_FAST_IDLE; + hdlc->bit_shift = 0; + } + break; + case HDLC_SEND_FAST_IDLE: + hdlc->do_closing = 0; + hdlc->cbin = 0xff; + hdlc->data_bits = 8; + if(hdlc->bit_shift == 8){ + hdlc->cbin = 0x7e; + hdlc->state = HDLC_SEND_FIRST_FLAG; + } else { + *dst++ = hdlc->cbin; + hdlc->bit_shift = hdlc->data_bits = 0; + len++; + dsize = 0; + } + break; + default: + break; + } + if(hdlc->do_adapt56){ + if(hdlc->data_bits==7){ + hdlc->cbin <<= 1; + hdlc->cbin++; + hdlc->data_bits++; + } + } + if(hdlc->data_bits==8){ + *dst++ = hdlc->cbin; + hdlc->data_bits = 0; + len++; + dsize--; + } + } + *count -= slen; + + return len; +} + diff --git a/drivers/isdn/hisax/st5481_hdlc.h b/drivers/isdn/hisax/st5481_hdlc.h new file mode 100644 index 000000000000..495432f0f6ba --- /dev/null +++ b/drivers/isdn/hisax/st5481_hdlc.h @@ -0,0 +1,62 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#ifndef __ST5481_HDLC_H__ +#define __ST5481_HDLC_H__ + +struct hdlc_vars { + int bit_shift; + int hdlc_bits1; + int data_bits; + int ffbit_shift; // encoding only + int state; + int dstpos; + + int data_received:1; // set if transferring data + int dchannel:1; // set if D channel (send idle instead of flags) + int do_adapt56:1; // set if 56K adaptation + int do_closing:1; // set if in closing phase (need to send CRC + flag + + unsigned short crc; + + unsigned char cbin; + unsigned char shift_reg; + unsigned char ffvalue; + +}; + + +/* + The return value from hdlc_decode is + the frame length, 0 if no complete frame was decoded, + or a negative error number +*/ + +#define HDLC_FRAMING_ERROR 1 +#define HDLC_CRC_ERROR 2 +#define HDLC_LENGTH_ERROR 3 + +void +hdlc_rcv_init(struct hdlc_vars *hdlc, int do_adapt56); + +int +hdlc_decode(struct hdlc_vars *hdlc, const unsigned char *src, int slen,int *count, + unsigned char *dst, int dsize); + +void +hdlc_out_init(struct hdlc_vars *hdlc,int is_d_channel,int do_adapt56); + +int +hdlc_encode(struct hdlc_vars *hdlc,const unsigned char *src,unsigned short slen,int *count, + unsigned char *dst,int dsize); + +#endif diff --git a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c new file mode 100644 index 000000000000..6e299ba936eb --- /dev/null +++ b/drivers/isdn/hisax/st5481_init.c @@ -0,0 +1,215 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* + * TODO: + * + * b layer1 delay? + * hdlc as module + * hotplug / unregister issues + * mod_inc/dec_use_count + * unify parts of d/b channel usb handling + * file header + * avoid copy to isoc buffer? + * improve usb delay? + * merge l1 state machines? + * clean up debug + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include "st5481.h" + +MODULE_AUTHOR("Frode Isaksen <fisaksen@bewan.com>"); +MODULE_DESCRIPTION("ST5481 USB ISDN modem driver"); + +static int protocol = 2; /* EURO-ISDN Default */ +MODULE_PARM(protocol, "i"); + +static int number_of_leds = 2; /* 2 LEDs on the adpater default */ +MODULE_PARM(number_of_leds, "i"); + +#ifdef CONFIG_HISAX_DEBUG +static int debug = 0x1; +MODULE_PARM(debug, "i"); +int st5481_debug; +#endif + +static LIST_HEAD(adapter_list); + +/* ====================================================================== + * registration/deregistration with the USB layer + */ + +/* + * This function will be called when the adapter is plugged + * into the USB bus. + */ +static void * __devinit probe_st5481(struct usb_device *dev, + unsigned int ifnum, + const struct usb_device_id *id) +{ + struct st5481_adapter *adapter; + struct hisax_b_if *b_if[2]; + int retval, i; + + printk(KERN_INFO "st541: found adapter VendorId %04x, ProductId %04x, LEDs %d\n", + dev->descriptor.idVendor, dev->descriptor.idProduct, + number_of_leds); + + adapter = kmalloc(sizeof(struct st5481_adapter), GFP_KERNEL); + if (!adapter) + return NULL; + + memset(adapter, 0, sizeof(struct st5481_adapter)); + + adapter->number_of_leds = number_of_leds; + adapter->usb_dev = dev; + + SET_MODULE_OWNER(&adapter->hisax_d_if); + adapter->hisax_d_if.ifc.priv = adapter; + adapter->hisax_d_if.ifc.l2l1 = st5481_d_l2l1; + + for (i = 0; i < 2; i++) { + adapter->bcs[i].adapter = adapter; + adapter->bcs[i].channel = i; + adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i]; + adapter->bcs[i].b_if.ifc.l2l1 = st5481_b_l2l1; + } + list_add(&adapter->list, &adapter_list); + + retval = st5481_setup_usb(adapter); + if (retval < 0) + goto err; + + retval = st5481_setup_d(adapter); + if (retval < 0) + goto err_usb; + + retval = st5481_setup_b(&adapter->bcs[0]); + if (retval < 0) + goto err_d; + + retval = st5481_setup_b(&adapter->bcs[1]); + if (retval < 0) + goto err_b; + + for (i = 0; i < 2; i++) + b_if[i] = &adapter->bcs[i].b_if; + + hisax_register(&adapter->hisax_d_if, b_if, "st5481_usb", protocol); + st5481_start(adapter); + + return adapter; + + err_b: + st5481_release_b(&adapter->bcs[0]); + err_d: + st5481_release_d(adapter); + err_usb: + st5481_release_usb(adapter); + err: + return NULL; +} + +/* + * This function will be called when the adapter is removed + * from the USB bus. + */ +static void __devexit disconnect_st5481(struct usb_device *dev, void *arg) +{ + struct st5481_adapter *adapter = arg; + + DBG(1,""); + + list_del(&adapter->list); + + st5481_stop(adapter); + st5481_release_b(&adapter->bcs[1]); + st5481_release_b(&adapter->bcs[0]); + st5481_release_d(adapter); + // we would actually better wait for completion of outstanding urbs + mdelay(2); + st5481_release_usb(adapter); + + hisax_unregister(&adapter->hisax_d_if); + + kfree(adapter); +} + +/* + * The last 4 bits in the Product Id is set with 4 pins on the chip. + */ +static struct usb_device_id st5481_ids[] = { + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x0) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x1) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x2) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x3) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x4) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x5) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x6) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x7) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x8) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x9) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xA) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xB) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xC) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xD) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xE) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xF) }, + { } +}; +MODULE_DEVICE_TABLE (usb, st5481_ids); + +static struct usb_driver st5481_usb_driver = { + name: "st5481_usb", + probe: probe_st5481, + disconnect: disconnect_st5481, + id_table: st5481_ids, +}; + +static int __init st5481_usb_init(void) +{ + int retval; + +#ifdef CONFIG_HISAX_DEBUG + st5481_debug = debug; +#endif + + printk(KERN_INFO "hiax_st5481: ST5481 USB ISDN driver v0.1.0\n"); + + retval = st5481_d_init(); + if (retval < 0) + goto out; + + retval = usb_register(&st5481_usb_driver); + if (retval < 0) + goto out_d_exit; + + return 0; + + out_d_exit: + st5481_d_exit(); + out: + return retval; +} + +static void __exit st5481_usb_exit(void) +{ + usb_deregister(&st5481_usb_driver); +} + +module_init(st5481_usb_init); +module_exit(st5481_usb_exit); diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c new file mode 100644 index 000000000000..9908094c41fc --- /dev/null +++ b/drivers/isdn/hisax/st5481_usb.c @@ -0,0 +1,639 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include "st5481.h" + +/* ====================================================================== + * control pipe + */ + +/* + * Send the next endpoint 0 request stored in the FIFO. + * Called either by the completion or by usb_ctrl_msg. + */ +static void usb_next_ctrl_msg(struct urb *urb, + struct st5481_adapter *adapter) +{ + struct st5481_ctrl *ctrl = &adapter->ctrl; + int r_index; + + if (test_and_set_bit(0, &ctrl->busy)) { + return; + } + + if ((r_index = fifo_remove(&ctrl->msg_fifo.f)) < 0) { + test_and_clear_bit(0,&ctrl->busy); + return; + } + urb->setup_packet = + (unsigned char *)&ctrl->msg_fifo.data[r_index]; + + DBG(1,"request=0x%02x,value=0x%04x,index=%x", + ((struct ctrl_msg *)urb->setup_packet)->dr.request, + ((struct ctrl_msg *)urb->setup_packet)->dr.value, + ((struct ctrl_msg *)urb->setup_packet)->dr.index); + + // Prepare the URB + urb->dev = adapter->usb_dev; + + SUBMIT_URB(urb); +} + +/* + * Asynchronous endpoint 0 request (async version of usb_control_msg). + * The request will be queued up in a FIFO if the endpoint is busy. + */ +void usb_ctrl_msg(struct st5481_adapter *adapter, + u8 request, u8 requesttype, u16 value, u16 index, + ctrl_complete_t complete, void *context) +{ + struct st5481_ctrl *ctrl = &adapter->ctrl; + int w_index; + struct ctrl_msg *ctrl_msg; + + if ((w_index = fifo_add(&ctrl->msg_fifo.f)) < 0) { + WARN("control msg FIFO full"); + return; + } + ctrl_msg = &ctrl->msg_fifo.data[w_index]; + + ctrl_msg->dr.requesttype = requesttype; + ctrl_msg->dr.request = request; + ctrl_msg->dr.value = cpu_to_le16p(&value); + ctrl_msg->dr.index = cpu_to_le16p(&index); + ctrl_msg->dr.length = 0; + ctrl_msg->complete = complete; + ctrl_msg->context = context; + + usb_next_ctrl_msg(ctrl->urb, adapter); +} + +/* + * Asynchronous endpoint 0 device request. + */ +void st5481_usb_device_ctrl_msg(struct st5481_adapter *adapter, + u8 request, u16 value, + ctrl_complete_t complete, void *context) +{ + usb_ctrl_msg(adapter, request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0, complete, context); +} + +/* + * Asynchronous pipe reset (async version of usb_clear_halt). + */ +void st5481_usb_pipe_reset(struct st5481_adapter *adapter, + u_char pipe, + ctrl_complete_t complete, void *context) +{ + DBG(1,"pipe=%02x",pipe); + + usb_ctrl_msg(adapter, + USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RECIP_ENDPOINT, + 0, pipe, complete, context); +} + + +/* + Physical level functions +*/ + +void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command) +{ + DBG(8,"command=%s", ST5481_CMD_string(command)); + + st5481_usb_device_ctrl_msg(adapter, TXCI, command, NULL, NULL); +} + +/* + * The request on endpoint 0 has completed. + * Call the user provided completion routine and try + * to send the next request. + */ +static void usb_ctrl_complete(struct urb *urb) +{ + struct st5481_adapter *adapter = urb->context; + struct st5481_ctrl *ctrl = &adapter->ctrl; + struct ctrl_msg *ctrl_msg; + + if (urb->status < 0) { + if (urb->status != USB_ST_URB_KILLED) { + WARN("urb status %d",urb->status); + } else { + DBG(1,"urb killed"); + return; // Give up + } + } + + ctrl_msg = (struct ctrl_msg *)urb->setup_packet; + + if (ctrl_msg->dr.request == USB_REQ_CLEAR_FEATURE) { + /* Special case handling for pipe reset */ + le16_to_cpus(&ctrl_msg->dr.index); + usb_endpoint_running(adapter->usb_dev, + ctrl_msg->dr.index & ~USB_DIR_IN, + (ctrl_msg->dr.index & USB_DIR_IN) == 0); + + /* toggle is reset on clear */ + usb_settoggle(adapter->usb_dev, + ctrl_msg->dr.index & ~USB_DIR_IN, + (ctrl_msg->dr.index & USB_DIR_IN) == 0, + 0); + + + } + + if (ctrl_msg->complete) + ctrl_msg->complete(ctrl_msg->context); + + clear_bit(0, &ctrl->busy); + + // Try to send next control message + usb_next_ctrl_msg(urb, adapter); + return; +} + +/* ====================================================================== + * interrupt pipe + */ + +/* + * The interrupt endpoint will be called when any + * of the 6 registers changes state (depending on masks). + * Decode the register values and schedule a private event. + * Called at interrupt. + */ +static void usb_int_complete(struct urb *urb) +{ + u_char *data = urb->transfer_buffer; + u_char irqbyte; + struct st5481_adapter *adapter = urb->context; + int j; + + if (urb->status < 0) { + if (urb->status != USB_ST_URB_KILLED) { + WARN("urb status %d",urb->status); + urb->actual_length = 0; + } else { + DBG(1,"urb killed"); + return; // Give up + } + } + + DBG_PACKET(1, data, INT_PKT_SIZE); + + if (urb->actual_length == 0) { + return; + } + + irqbyte = data[MPINT]; + if (irqbyte & DEN_INT) + FsmEvent(&adapter->d_out.fsm, EV_DOUT_DEN, NULL); + + if (irqbyte & DCOLL_INT) + FsmEvent(&adapter->d_out.fsm, EV_DOUT_COLL, NULL); + + irqbyte = data[FFINT_D]; + if (irqbyte & OUT_UNDERRUN) + FsmEvent(&adapter->d_out.fsm, EV_DOUT_UNDERRUN, NULL); + + if (irqbyte & OUT_DOWN) +;// printk("OUT_DOWN\n"); + + irqbyte = data[MPINT]; + if (irqbyte & RXCI_INT) + FsmEvent(&adapter->l1m, data[CCIST] & 0x0f, NULL); + + for (j = 0; j < 2; j++) + adapter->bcs[j].b_out.flow_event |= data[FFINT_B1 + j]; + + urb->actual_length = 0; +} + +/* ====================================================================== + * initialization + */ + +int __devinit st5481_setup_usb(struct st5481_adapter *adapter) +{ + struct usb_device *dev = adapter->usb_dev; + struct st5481_ctrl *ctrl = &adapter->ctrl; + struct st5481_intr *intr = &adapter->intr; + struct usb_interface_descriptor *altsetting; + struct usb_endpoint_descriptor *endpoint; + int status; + urb_t *urb; + u_char *buf; + + DBG(1,""); + + if ((status = usb_set_configuration (dev,dev->config[0].bConfigurationValue)) < 0) { + WARN("set_configuration failed,status=%d",status); + return status; + } + + + altsetting = &(dev->config->interface[0].altsetting[3]); + + // Check if the config is sane + if ( altsetting->bNumEndpoints != 7 ) { + WARN("expecting 7 got %d endpoints!", altsetting->bNumEndpoints); + return -EINVAL; + } + + // The descriptor is wrong for some early samples of the ST5481 chip + altsetting->endpoint[3].wMaxPacketSize = 32; + altsetting->endpoint[4].wMaxPacketSize = 32; + + // Use alternative setting 3 on interface 0 to have 2B+D + if ((status = usb_set_interface (dev, 0, 3)) < 0) { + WARN("usb_set_interface failed,status=%d",status); + return status; + } + + // Allocate URB for control endpoint + urb = usb_alloc_urb(0); + if (!urb) { + return -ENOMEM; + } + ctrl->urb = urb; + + // Fill the control URB + FILL_CONTROL_URB (urb, dev, + usb_sndctrlpipe(dev, 0), + NULL, NULL, 0, usb_ctrl_complete, adapter); + + + fifo_init(&ctrl->msg_fifo.f, ARRAY_SIZE(ctrl->msg_fifo.data)); + + // Allocate URBs and buffers for interrupt endpoint + urb = usb_alloc_urb(0); + if (!urb) { + return -ENOMEM; + } + intr->urb = urb; + + buf = kmalloc(INT_PKT_SIZE, GFP_KERNEL); + if (!buf) { + return -ENOMEM; + } + + endpoint = &altsetting->endpoint[EP_INT-1]; + + // Fill the interrupt URB + FILL_INT_URB(urb, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + buf, INT_PKT_SIZE, + usb_int_complete, adapter, + endpoint->bInterval); + + return 0; +} + +/* + * Release buffers and URBs for the interrupt and control + * endpoint. + */ +void __devexit st5481_release_usb(struct st5481_adapter *adapter) +{ + struct st5481_intr *intr = &adapter->intr; + struct st5481_ctrl *ctrl = &adapter->ctrl; + + DBG(1,""); + + // Stop and free Control and Interrupt URBs + usb_unlink_urb(ctrl->urb); + if (ctrl->urb->transfer_buffer) + kfree(ctrl->urb->transfer_buffer); + usb_free_urb(ctrl->urb); + + usb_unlink_urb(intr->urb); + if (intr->urb->transfer_buffer) + kfree(intr->urb->transfer_buffer); + usb_free_urb(intr->urb); +} + +/* + * Initialize the adapter. + */ +void __devinit st5481_start(struct st5481_adapter *adapter) +{ + static const u8 init_cmd_table[]={ + SET_DEFAULT,0, + STT,0, + SDA_MIN,0x0d, + SDA_MAX,0x29, + SDELAY_VALUE,0x14, + GPIO_DIR,0x01, + GPIO_OUT,RED_LED, +// FFCTRL_OUT_D,4, +// FFCTRH_OUT_D,12, + FFCTRL_OUT_B1,6, + FFCTRH_OUT_B1,20, + FFCTRL_OUT_B2,6, + FFCTRH_OUT_B2,20, + MPMSK,RXCI_INT+DEN_INT+DCOLL_INT, + 0 + }; + struct st5481_intr *intr = &adapter->intr; + int i = 0; + u8 request,value; + + DBG(8,""); + + adapter->leds = RED_LED; + + // Start receiving on the interrupt endpoint + SUBMIT_URB(intr->urb); + + while ((request = init_cmd_table[i++])) { + value = init_cmd_table[i++]; + st5481_usb_device_ctrl_msg(adapter, request, value, NULL, NULL); + } + st5481_ph_command(adapter, ST5481_CMD_PUP); +} + +/* + * Reset the adapter to default values. + */ +void __devexit st5481_stop(struct st5481_adapter *adapter) +{ + DBG(8,""); + + st5481_usb_device_ctrl_msg(adapter, SET_DEFAULT, 0, NULL, NULL); +} + +/* ====================================================================== + * isochronous USB helpers + */ + +static void __devinit +fill_isoc_urb(struct urb *urb, struct usb_device *dev, + unsigned int pipe, void *buf, int num_packets, + int packet_size, usb_complete_t complete, + void *context) +{ + int k; + + spin_lock_init(&urb->lock); + urb->dev=dev; + urb->pipe=pipe; + urb->transfer_buffer=buf; + urb->number_of_packets = num_packets; + urb->transfer_buffer_length=num_packets*packet_size; + urb->actual_length = 0; + urb->complete=complete; + urb->context=context; + urb->transfer_flags=USB_ISO_ASAP; + for (k = 0; k < num_packets; k++) { + urb->iso_frame_desc[k].offset = packet_size * k; + urb->iso_frame_desc[k].length = packet_size; + urb->iso_frame_desc[k].actual_length = 0; + } +} + +int __devinit +st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev, + unsigned int pipe, int num_packets, + int packet_size, int buf_size, + usb_complete_t complete, void *context) +{ + int j, retval; + unsigned char *buf; + + for (j = 0; j < 2; j++) { + retval = -ENOMEM; + urb[j] = usb_alloc_urb(num_packets); + if (!urb[j]) + goto err; + + // Allocate memory for 2000bytes/sec (16Kb/s) + buf = kmalloc(buf_size, GFP_KERNEL); + if (!buf) + goto err; + + // Fill the isochronous URB + fill_isoc_urb(urb[j], dev, pipe, buf, + num_packets, packet_size, complete, + context); + } + return 0; + + err: + for (j = 0; j < 2; j++) { + if (urb[j]) { + if (urb[j]->transfer_buffer) + kfree(urb[j]->transfer_buffer); + usb_free_urb(urb[j]); + } + } + return retval; +} + +void __devexit st5481_release_isocpipes(struct urb* urb[2]) +{ + int j; + + for (j = 0; j < 2; j++) { + usb_unlink_urb(urb[j]); + if (urb[j]->transfer_buffer) + kfree(urb[j]->transfer_buffer); + usb_free_urb(urb[j]); + } +} + +/* + * Decode frames received on the B/D channel. + * Note that this function will be called continously + * with 64Kbit/s / 16Kbit/s of data and hence it will be + * called 50 times per second with 20 ISOC descriptors. + * Called at interrupt. + */ +static void usb_in_complete(struct urb *urb) +{ + struct st5481_in *in = urb->context; + unsigned char *ptr; + struct sk_buff *skb; + int len, count, status; + + if (urb->status < 0) { + if (urb->status != USB_ST_URB_KILLED) { + WARN("urb status %d",urb->status); + } else { + DBG(1,"urb killed"); + return; // Give up + } + } + + DBG_ISO_PACKET(0x80,urb); + + len = st5481_isoc_flatten(urb); + ptr = urb->transfer_buffer; + while (len > 0) { + if (in->mode == L1_MODE_TRANS) { + memcpy(in->rcvbuf, ptr, len); + status = len; + len = 0; + } else { + status = hdlc_decode(&in->hdlc_state, ptr, len, &count, + in->rcvbuf, in->bufsize); + ptr += count; + len -= count; + } + + if (status > 0) { + // Good frame received + DBG(4,"count=%d",status); + DBG_PACKET(0x400, in->rcvbuf, status); + if (!(skb = dev_alloc_skb(status))) { + WARN("receive out of memory\n"); + break; + } + memcpy(skb_put(skb, status), in->rcvbuf, status); + in->hisax_if->l1l2(in->hisax_if, PH_DATA | INDICATION, skb); + } else if (status == -HDLC_CRC_ERROR) { + INFO("CRC error"); + } else if (status == -HDLC_FRAMING_ERROR) { + INFO("framing error"); + } else if (status == -HDLC_LENGTH_ERROR) { + INFO("length error"); + } + } + + // Prepare URB for next transfer + urb->dev = in->adapter->usb_dev; + urb->actual_length = 0; + + SUBMIT_URB(urb); +} + +int __devinit st5481_setup_in(struct st5481_in *in) +{ + struct usb_device *dev = in->adapter->usb_dev; + int retval; + + DBG(4,""); + + in->rcvbuf = kmalloc(in->bufsize, GFP_KERNEL); + retval = -ENOMEM; + if (!in->rcvbuf) + goto err; + + retval = st5481_setup_isocpipes(in->urb, dev, + usb_rcvisocpipe(dev, in->ep), + in->num_packets, in->packet_size, + in->num_packets * in->packet_size, + usb_in_complete, in); + if (retval) + goto err_free; + return 0; + + err_free: + kfree(in->rcvbuf); + err: + return retval; +} + +void __devexit st5481_release_in(struct st5481_in *in) +{ + DBG(2,""); + + st5481_release_isocpipes(in->urb); +} + +/* + * Make the transfer_buffer contiguous by + * copying from the iso descriptors if necessary. + */ +int st5481_isoc_flatten(struct urb *urb) +{ + piso_packet_descriptor_t pipd,pend; + unsigned char *src,*dst; + unsigned int len; + + if (urb->status < 0) { + return urb->status; + } + for (pipd = &urb->iso_frame_desc[0], + pend = &urb->iso_frame_desc[urb->number_of_packets], + dst = urb->transfer_buffer; + pipd < pend; + pipd++) { + + if (pipd->status < 0) { + return (pipd->status); + } + + len = pipd->actual_length; + pipd->actual_length = 0; + src = urb->transfer_buffer+pipd->offset; + + if (src != dst) { + // Need to copy since isoc buffers not full + while (len--) { + *dst++ = *src++; + } + } else { + // No need to copy, just update destination buffer + dst += len; + } + } + // Return size of flattened buffer + return (dst - (unsigned char *)urb->transfer_buffer); +} + +static void st5481_start_rcv(void *context) +{ + struct st5481_in *in = context; + struct st5481_adapter *adapter = in->adapter; + + DBG(4,""); + + in->urb[0]->dev = adapter->usb_dev; + SUBMIT_URB(in->urb[0]); + + in->urb[1]->dev = adapter->usb_dev; + SUBMIT_URB(in->urb[1]); +} + +void st5481_in_mode(struct st5481_in *in, int mode) +{ + if (in->mode == mode) + return; + + in->mode = mode; + + usb_unlink_urb(in->urb[0]); + usb_unlink_urb(in->urb[1]); + + if (in->mode != L1_MODE_NULL) { + if (in->mode != L1_MODE_TRANS) + hdlc_rcv_init(&in->hdlc_state, + in->mode == L1_MODE_HDLC_56K); + + st5481_usb_pipe_reset(in->adapter, in->ep, NULL, NULL); + st5481_usb_device_ctrl_msg(in->adapter, in->counter, + in->packet_size, + NULL, NULL); + st5481_start_rcv(in); + } else { + st5481_usb_device_ctrl_msg(in->adapter, in->counter, + 0, NULL, NULL); + } +} + diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index 5e90a5446a19..e9601e0364cb 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -1,4 +1,4 @@ -/* $Id: isdn_common.c,v 1.114.6.13 2001/08/13 07:46:15 kai Exp $ +/* $Id: isdn_common.c,v 1.114.6.14 2001/08/17 12:34:25 kai Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -51,7 +51,7 @@ isdn_dev *dev; -static char *isdn_revision = "$Revision: 1.114.6.13 $"; +static char *isdn_revision = "$Revision: 1.114.6.14 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -792,7 +792,6 @@ isdn_getnum(char **p) int isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep) { - int left; int count; int count_pull; int count_put; @@ -808,10 +807,11 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_que else return 0; } - left = MIN(len, dev->drv[di]->rcvcount[channel]); + if (len > dev->drv[di]->rcvcount[channel]) + len = dev->drv[di]->rcvcount[channel]; cp = buf; count = 0; - while (left) { + while (len) { if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel]))) break; #ifdef CONFIG_ISDN_AUDIO @@ -824,8 +824,8 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_que dflag = 0; count_pull = count_put = 0; - while ((count_pull < skb->len) && (left > 0)) { - left--; + while ((count_pull < skb->len) && (len > 0)) { + len--; if (dev->drv[di]->DLEflag & DLEmask) { *cp++ = DLE; dev->drv[di]->DLEflag &= ~DLEmask; @@ -846,14 +846,14 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_que #endif /* No DLE's in buff, so simply copy it */ dflag = 1; - if ((count_pull = skb->len) > left) { - count_pull = left; + if ((count_pull = skb->len) > len) { + count_pull = len; dflag = 0; } count_put = count_pull; memcpy(cp, skb->data, count_put); cp += count_put; - left -= count_put; + len -= count_put; #ifdef CONFIG_ISDN_AUDIO } #endif @@ -1048,12 +1048,15 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off) } interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq)); } - if (dev->drv[drvidx]->interface->readstat) + if (dev->drv[drvidx]->interface->readstat) { + if (count > dev->drv[drvidx]->stavail) + count = dev->drv[drvidx]->stavail; len = dev->drv[drvidx]->interface-> - readstat(buf, MIN(count, dev->drv[drvidx]->stavail), - 1, drvidx, isdn_minor2chan(minor)); - else + readstat(buf, count, 1, drvidx, + isdn_minor2chan(minor)); + } else { len = 0; + } save_flags(flags); cli(); if (len) diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index 8fcfdc543dcc..3f77f9c22cca 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.140.6.7 2001/07/27 11:15:53 kai Exp $ +/* $Id: isdn_net.c,v 1.140.6.8 2001/08/14 14:04:21 kai Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -190,7 +190,7 @@ static int isdn_net_start_xmit(struct sk_buff *, struct net_device *); static void isdn_net_ciscohdlck_connected(isdn_net_local *lp); static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp); -char *isdn_net_revision = "$Revision: 1.140.6.7 $"; +char *isdn_net_revision = "$Revision: 1.140.6.8 $"; /* * Code for raw-networking over ISDN @@ -1755,6 +1755,11 @@ isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb) case CISCO_TYPE_SLARP: isdn_net_ciscohdlck_slarp_in(lp, skb); goto out_free; + case CISCO_TYPE_CDP: + if (lp->cisco_debserint) + printk(KERN_DEBUG "%s: Received CDP packet. use " + "\"no cdp enable\" on cisco.\n", lp->name); + goto out_free; default: printk(KERN_WARNING "%s: Unknown Cisco type 0x%04x\n", lp->name, type); diff --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h index 21adcf87be73..7b4a25baeb98 100644 --- a/drivers/isdn/isdn_net.h +++ b/drivers/isdn/isdn_net.h @@ -1,4 +1,4 @@ -/* $Id: isdn_net.h,v 1.19.6.1 2001/04/20 02:41:58 keil Exp $ +/* $Id: isdn_net.h,v 1.19.6.2 2001/08/14 14:04:21 kai Exp $ * header for Linux ISDN subsystem, network related functions (linklevel). * @@ -36,6 +36,7 @@ #define CISCO_ADDR_UNICAST 0x0f #define CISCO_ADDR_BROADCAST 0x8f #define CISCO_CTRL 0x00 +#define CISCO_TYPE_CDP 0x2000 #define CISCO_TYPE_INET 0x0800 #define CISCO_TYPE_SLARP 0x8035 #define CISCO_SLARP_REPLY 0 diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index d0b4baf52e5b..a1e6d1b4180a 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.c,v 1.94.6.4 2001/07/27 11:15:53 kai Exp $ +/* $Id: isdn_tty.c,v 1.94.6.7 2001/08/27 22:19:04 kai Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -66,7 +66,7 @@ static int bit2si[8] = static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.94.6.4 $"; +char *isdn_tty_revision = "$Revision: 1.94.6.7 $"; /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() @@ -727,9 +727,19 @@ void isdn_tty_modem_hup(modem_info * info, int local) { isdn_ctrl cmd; + int di, ch; if (!info) return; + + di = info->isdn_driver; + ch = info->isdn_channel; + if (di < 0 || ch < 0) + return; + + info->isdn_driver = -1; + info->isdn_channel = -1; + #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup ttyI%d\n", info->line); #endif @@ -770,19 +780,18 @@ isdn_tty_modem_hup(modem_info * info, int local) isdn_tty_modem_result(RESULT_RUNG, info); info->msr &= ~(UART_MSR_DCD | UART_MSR_RI); info->lsr |= UART_LSR_TEMT; - if (info->isdn_driver >= 0) { - if (local) { - cmd.driver = info->isdn_driver; - cmd.command = ISDN_CMD_HANGUP; - cmd.arg = info->isdn_channel; - isdn_command(&cmd); - } - isdn_all_eaz(info->isdn_driver, info->isdn_channel); - info->emu.mdmreg[REG_RINGCNT] = 0; - isdn_free_channel(info->isdn_driver, info->isdn_channel, 0); + + if (local) { + cmd.driver = di; + cmd.command = ISDN_CMD_HANGUP; + cmd.arg = ch; + isdn_command(&cmd); } - info->isdn_driver = -1; - info->isdn_channel = -1; + + isdn_all_eaz(di, ch); + info->emu.mdmreg[REG_RINGCNT] = 0; + isdn_free_channel(di, ch, 0); + if (info->drv_index >= 0) { dev->m_idx[info->drv_index] = -1; info->drv_index = -1; @@ -1187,9 +1196,11 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co /* See isdn_tty_senddown() */ atomic_inc(&info->xmit_lock); while (1) { - c = MIN(count, info->xmit_size - info->xmit_count); - if (info->isdn_driver >= 0) - c = MIN(c, dev->drv[info->isdn_driver]->maxbufsize); + c = count; + if (c > info->xmit_size - info->xmit_count) + c = info->xmit_size - info->xmit_count; + if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize) + c = dev->drv[info->isdn_driver]->maxbufsize; if (c <= 0) break; if ((info->online > 1) @@ -2709,6 +2720,10 @@ isdn_tty_modem_result(int code, modem_info * info) if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) { isdn_tty_at_cout("\r\nCALLER NUMBER: ", info); isdn_tty_at_cout(dev->num[info->drv_index], info); + if (m->mdmreg[REG_CDN] & BIT_CDN) { + isdn_tty_at_cout("\r\nCALLED NUMBER: ", info); + isdn_tty_at_cout(info->emu.cpn, info); + } } } isdn_tty_at_cout("\r\n", info); @@ -2734,6 +2749,10 @@ isdn_tty_modem_result(int code, modem_info * info) isdn_tty_at_cout("\r\n", info); isdn_tty_at_cout("CALLER NUMBER: ", info); isdn_tty_at_cout(dev->num[info->drv_index], info); + if (m->mdmreg[REG_CDN] & BIT_CDN) { + isdn_tty_at_cout("\r\nCALLED NUMBER: ", info); + isdn_tty_at_cout(info->emu.cpn, info); + } } break; case RESULT_NO_CARRIER: diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h index 4553cf5687b2..e2ce92c38d18 100644 --- a/drivers/isdn/isdn_tty.h +++ b/drivers/isdn/isdn_tty.h @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.h,v 1.22 2000/06/21 09:54:29 keil Exp $ +/* $Id: isdn_tty.h,v 1.22.6.1 2001/08/14 14:12:18 kai Exp $ * header for Linux ISDN subsystem, tty related functions (linklevel). * @@ -85,7 +85,10 @@ #define REG_CPN 23 #define BIT_CPN 1 +#define REG_CPNFCON 23 #define BIT_CPNFCON 2 +#define REG_CDN 23 +#define BIT_CDN 4 /* defines for result codes */ #define RESULT_OK 0 diff --git a/drivers/isdn/isdn_ttyfax.c b/drivers/isdn/isdn_ttyfax.c index 8899ce5a9a4c..0e6b9a35908f 100644 --- a/drivers/isdn/isdn_ttyfax.c +++ b/drivers/isdn/isdn_ttyfax.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ttyfax.c,v 1.7 2000/05/11 22:29:21 kai Exp $ +/* $Id: isdn_ttyfax.c,v 1.7.6.1 2001/08/14 14:12:18 kai Exp $ * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel). * @@ -33,7 +33,7 @@ #include "isdn_ttyfax.h" -static char *isdn_tty_fax_revision = "$Revision: 1.7 $"; +static char *isdn_tty_fax_revision = "$Revision: 1.7.6.1 $"; #define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; } @@ -86,7 +86,7 @@ isdn_tty_fax_modem_result(int code, modem_info * info) break; case 2: /* +FCON */ /* Append CPN, if enabled */ - if ((m->mdmreg[REG_CPN] & BIT_CPNFCON) && + if ((m->mdmreg[REG_CPNFCON] & BIT_CPNFCON) && (!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) { sprintf(rs, "/%s", m->cpn); isdn_tty_at_cout(rs, info); diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index 5f0f14a76c02..73f4888632f4 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -1,4 +1,4 @@ -/* $Id: isdnloop.c,v 1.11.6.4 2001/07/13 09:20:12 kai Exp $ +/* $Id: isdnloop.c,v 1.11.6.5 2001/08/17 12:34:27 kai Exp $ * ISDN low-level module implementing a dummy loop driver. * @@ -26,7 +26,7 @@ #include "isdnloop.h" static char -*revision = "$Revision: 1.11.6.4 $"; +*revision = "$Revision: 1.11.6.5 $"; static int isdnloop_addcard(char *); @@ -985,10 +985,12 @@ isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card) isdn_ctrl cmd; while (len) { - int count = MIN(255, len); + int count = len; u_char *p; u_char msg[0x100]; + if (count > 255) + count = 255; if (user) copy_from_user(msg, buf, count); else diff --git a/drivers/isdn/isdnloop/isdnloop.h b/drivers/isdn/isdnloop/isdnloop.h index fe8a501104ff..1a59ba57dc87 100644 --- a/drivers/isdn/isdnloop/isdnloop.h +++ b/drivers/isdn/isdnloop/isdnloop.h @@ -1,4 +1,4 @@ -/* $Id: isdnloop.h,v 1.5.6.1 2001/02/10 14:41:23 kai Exp $ +/* $Id: isdnloop.h,v 1.5.6.2 2001/08/17 12:34:27 kai Exp $ * Loopback lowlevel module for testing of linklevel. * @@ -128,8 +128,6 @@ MODULE_PARM_DESC(isdnloop_id, "ID-String of first card"); /* Utility-Macros */ #define CID (card->interface.id) -#define MIN(a,b) ((a<b)?a:b) -#define MAX(a,b) ((a>b)?a:b) #endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ #endif /* isdnloop_h */ diff --git a/drivers/md/lvm.c b/drivers/md/lvm.c index 7d42369854b9..cb830201c87d 100644 --- a/drivers/md/lvm.c +++ b/drivers/md/lvm.c @@ -376,17 +376,13 @@ static int lvm_size[MAX_LV] = {0,}; static struct gendisk lvm_gendisk = { - MAJOR_NR, /* major # */ - LVM_NAME, /* name of major */ - 0, /* number of times minor is shifted - to get real minor */ - 1, /* maximum partitions per device */ - lvm_hd_struct, /* partition table */ - lvm_size, /* device size in blocks, copied - to block_size[] */ - MAX_LV, /* number or real devices */ - NULL, /* internal */ - NULL, /* pointer to next gendisk struct (internal) */ + major: MAJOR_NR, + major_name: LVM_NAME, + minor_shift: 0, + max_p: 1, + part: lvm_hd_struct, + sizes: lvm_size, + nr_real: MAX_LV, }; /* diff --git a/drivers/media/video/zr36067.c b/drivers/media/video/zr36067.c index c8724990abce..1cf403df7fdf 100644 --- a/drivers/media/video/zr36067.c +++ b/drivers/media/video/zr36067.c @@ -3734,7 +3734,6 @@ static int do_zoran_ioctl(struct zoran *zr, unsigned int cmd, zr->window.chromakey = 0; zr->window.flags = 0; // RJ: Is this intended for interlace on/off ? zr->window.clips = NULL; - zr->window.clipcount = vw.clipcount; /* * If an overlay is running, we have to switch it off @@ -3775,7 +3774,8 @@ static int do_zoran_ioctl(struct zoran *zr, unsigned int cmd, write_overlay_mask(zr, vcp, vw.clipcount); vfree(vcp); } - + zr->window.clipcount = vw.clipcount; + if (on) zr36057_overlay(zr, 1); zr->window_set = 1; diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c index 8f582fd635f9..ce1bb92fe7b6 100644 --- a/drivers/media/video/zr36120.c +++ b/drivers/media/video/zr36120.c @@ -1056,7 +1056,7 @@ int zoran_ioctl(struct video_device* dev, unsigned int cmd, void *arg) DEBUG(printk(CARD_DEBUG "VIDIOCSCHAN(%d,%d)\n",CARD,v.channel,v.norm)); /* too many inputs? no decoder -> no channels */ - if (!ztv->have_decoder || v.channel >= ztv->card->video_inputs) + if (!ztv->have_decoder || v.channel >= ztv->card->video_inputs || v.channel < 0) return -EINVAL; if (v.norm != VIDEO_MODE_PAL && diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 21b3aa27f5d0..4bec8ae88122 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -1024,11 +1024,6 @@ static struct block_device_operations nftl_fops = * ****************************************************************************/ -#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) -#define init_nftl init_module -#define cleanup_nftl cleanup_module -#endif - static struct mtd_notifier nftl_notifier = { add: NFTL_notify_add, remove: NFTL_notify_remove @@ -1045,14 +1040,12 @@ static int __init init_nftl(void) #endif if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){ - printk("unable to register NFTL block device on major %d\n", MAJOR_NR); + printk("unable to register NFTL block device on major %d\n", + MAJOR_NR); return -EBUSY; } else { -#if LINUX_VERSION_CODE < 0x20320 - blk_dev[MAJOR_NR].request_fn = nftl_request; -#else blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request); -#endif + /* set block size to 1kB each */ for (i = 0; i < 256; i++) { nftl_blocksizes[i] = 1024; @@ -1069,20 +1062,12 @@ static int __init init_nftl(void) static void __exit cleanup_nftl(void) { - struct gendisk *gd, **gdp; - unregister_mtd_user(&nftl_notifier); unregister_blkdev(MAJOR_NR, "nftl"); -#if LINUX_VERSION_CODE < 0x20320 - blk_dev[MAJOR_NR].request_fn = 0; -#else blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); -#endif - /* remove ourself from generic harddisk list - FIXME: why can't I found this partition on /proc/partition */ - del_gendisk(&nftl_gendisk);: + del_gendisk(&nftl_gendisk); } module_init(init_nftl); diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index a82d4dcb0f71..e2586618b919 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -1013,6 +1013,8 @@ static struct net_device_stats *cops_get_stats(struct net_device *dev) #ifdef MODULE static struct net_device cops0_dev = { init: cops_probe }; + +MODULE_LICENSE("GPL"); MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); MODULE_PARM(board_type, "i"); diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index acd15efa937e..c5a994577f3d 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -283,6 +283,7 @@ static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static struct net_device dev_ipddp; +MODULE_LICENSE("GPL"); MODULE_PARM(ipddp_mode, "i"); static int __init ipddp_init_module(void) diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c index 6c5560fe6f24..579c3b3cd6ec 100644 --- a/drivers/net/appletalk/ltpc.c +++ b/drivers/net/appletalk/ltpc.c @@ -1255,6 +1255,8 @@ __setup("ltpc=", ltpc_setup); static struct net_device dev_ltpc; #ifdef MODULE + +MODULE_LICENSE("GPL"); MODULE_PARM(debug, "i"); MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 3ebf32ab1771..168b9dd1543f 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -39,7 +39,7 @@ #include <linux/errno.h> #include <linux/in.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/init.h> diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 0d2ad55b5509..42969a787451 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -1111,7 +1111,7 @@ net_open(struct net_device *dev) (unsigned long)lp->dma_buff, (unsigned long)virt_to_bus(lp->dma_buff)); } - if ((unsigned long)virt_to_bus(lp->dma_buff) >= MAX_DMA_ADDRESS || + if ((unsigned long) lp->dma_buff >= MAX_DMA_ADDRESS || !dma_page_eq(lp->dma_buff, lp->dma_buff+lp->dmasize*1024-1)) { printk(KERN_ERR "%s: not usable as DMA buffer\n", dev->name); goto release_irq; diff --git a/drivers/net/declance.c b/drivers/net/declance.c index 7a86e003c5c1..173e42b4af39 100644 --- a/drivers/net/declance.c +++ b/drivers/net/declance.c @@ -1006,7 +1006,7 @@ static void lance_set_multicast_retry(unsigned long _opaque) lance_set_multicast(dev); } -static int __init dec_lance_init(const int type) +static int __init dec_lance_init(struct net_device *dev, const int type) { static unsigned version_printed; struct net_device *dev; @@ -1015,7 +1015,6 @@ static int __init dec_lance_init(const int type) int i, ret; unsigned long esar_base; unsigned char *esar; - struct net_device *dev; #ifndef CONFIG_TC system_base = KN01_LANCE_BASE; @@ -1026,12 +1025,12 @@ static int __init dec_lance_init(const int type) if (dec_lance_debug && version_printed++ == 0) printk(version); - dev = init_etherdev(NULL, sizeof(struct lance_private)); + dev = init_etherdev(0, sizeof(struct lance_private)); if (!dev) return -ENOMEM; /* init_etherdev ensures the data structures used by the LANCE are aligned. */ - lp = dev->priv; + lp = (struct lance_private *) dev->priv; spin_lock_init(&lp->lock); switch (type) { @@ -1217,6 +1216,7 @@ err_out: /* Find all the lance cards on the system and initialize them */ static int __init dec_lance_probe(void) { + struct net_device *dev = NULL; static int called; #ifdef MODULE @@ -1255,7 +1255,7 @@ static int __init dec_lance_probe(void) } #endif - return dec_lance_init(type); + return dec_lance_init(dev, type); } static void __exit dec_lance_cleanup(void) diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c index 542f526a004d..7a6f1385cfe8 100644 --- a/drivers/net/dgrs.c +++ b/drivers/net/dgrs.c @@ -1292,6 +1292,7 @@ dgrs_found_device( if (!devN) goto fail; memcpy(devN, dev, dev_size); + memset(devN->name, 0, sizeof(devN->name)); devN->priv = ((void *)devN) + sizeof(struct net_device); privN = (DGRS_PRIV *)devN->priv; /* ... and zero out VM areas */ diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c index 89a2871d38a7..16e1d97b403d 100644 --- a/drivers/net/dmfe.c +++ b/drivers/net/dmfe.c @@ -61,7 +61,7 @@ #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/init.h> diff --git a/drivers/net/gt96100eth.c b/drivers/net/gt96100eth.c index 948a318e71b8..38f448bf3ea8 100644 --- a/drivers/net/gt96100eth.c +++ b/drivers/net/gt96100eth.c @@ -1,7 +1,7 @@ /* * Copyright 2000 MontaVista Software Inc. * Author: MontaVista Software, Inc. - * stevel@mvista.com or support@mvista.com + * stevel@mvista.com or source@mvista.com * * ######################################################################## * @@ -29,7 +29,6 @@ #endif -#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -38,7 +37,7 @@ #include <linux/errno.h> #include <linux/in.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/init.h> diff --git a/drivers/net/gt96100eth.h b/drivers/net/gt96100eth.h index 2664aa14cdab..dfbc4b13f880 100644 --- a/drivers/net/gt96100eth.h +++ b/drivers/net/gt96100eth.h @@ -1,7 +1,7 @@ /* * Copyright 2000 MontaVista Software Inc. * Author: MontaVista Software, Inc. - * stevel@mvista.com or support@mvista.com + * stevel@mvista.com or source@mvista.com * * ######################################################################## * @@ -27,7 +27,6 @@ #ifndef _GT96100ETH_H #define _GT96100ETH_H -#include <linux/config.h> #include <asm/galileo-boards/gt96100.h> /* Keep the ring sizes a power of two for efficiency. */ diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 3b52d93592a2..8036cc4a387e 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -44,7 +44,6 @@ #include <net/irda/irda-usb.h> -static u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */ static int qos_mtt_bits = 0; static void irda_usb_dump_class_desc(struct irda_class_desc *desc); diff --git a/drivers/net/irda/toshoboe.c b/drivers/net/irda/toshoboe.c index 467f51ff63ab..51d0f8d24d60 100644 --- a/drivers/net/irda/toshoboe.c +++ b/drivers/net/irda/toshoboe.c @@ -695,7 +695,6 @@ toshoboe_probe (struct pci_dev *pci_dev, const struct pci_device_id *pdid) { struct toshoboe_cb *self; struct net_device *dev; - struct pm_dev *pmdev; int i = 0; int ok = 0; int err; @@ -845,11 +844,6 @@ toshoboe_probe (struct pci_dev *pci_dev, const struct pci_device_id *pdid) } pci_set_drvdata(pci_dev,self); -/* pmdev = pm_register (PM_PCI_DEV, PM_PCI_ID(pci_dev), toshoboe_pmproc); - if (pmdev) - pmdev->data = self; - */ - printk (KERN_WARNING "ToshOboe: Using "); #ifdef ONETASK printk ("single"); diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c index 1939f61b3567..3489ffcc82e2 100644 --- a/drivers/net/jazzsonic.c +++ b/drivers/net/jazzsonic.c @@ -47,11 +47,12 @@ /* * Macros to access SONIC registers */ -#define SONIC_READ(reg) \ - *((volatile unsigned int *)base_addr+reg) +#define SONIC_READ(reg) (*((volatile unsigned int *)base_addr+reg)) -#define SONIC_WRITE(reg,val) \ - *((volatile unsigned int *)base_addr+reg) = val +#define SONIC_WRITE(reg,val) \ +do { \ + *((volatile unsigned int *)base_addr+reg) = val; \ +} /* use 0 for production, 1 for verification, >2 for debug */ @@ -65,8 +66,8 @@ static unsigned int sonic_debug = 1; * Base address and interrupt of the SONIC controller on JAZZ boards */ static struct { - unsigned int port; - unsigned int irq; + unsigned int port; + unsigned int irq; } sonic_portlist[] = { {JAZZ_ETHERNET_BASE, JAZZ_ETHERNET_IRQ}, {0, 0}}; /* @@ -76,14 +77,15 @@ static struct { */ static unsigned short known_revisions[] = { - 0x04, /* Mips Magnum 4000 */ - 0xffff /* end of list */ + 0x04, /* Mips Magnum 4000 */ + 0xffff /* end of list */ }; /* Index to functions, as function prototypes. */ extern int sonic_probe(struct net_device *dev); -static int sonic_probe1(struct net_device *dev, unsigned int base_addr, unsigned int irq); +static int sonic_probe1(struct net_device *dev, unsigned int base_addr, + unsigned int irq); /* @@ -92,164 +94,170 @@ static int sonic_probe1(struct net_device *dev, unsigned int base_addr, unsigned */ int __init sonic_probe(struct net_device *dev) { - unsigned int base_addr = dev ? dev->base_addr : 0; - int i; + unsigned int base_addr = dev ? dev->base_addr : 0; + int i; + + /* + * Don't probe if we're not running on a Jazz board. + */ + if (mips_machgroup != MACH_GROUP_JAZZ) + return -ENODEV; + if (base_addr >= KSEG0) /* Check a single specified location. */ + return sonic_probe1(dev, base_addr, dev->irq); + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; - /* - * Don't probe if we're not running on a Jazz board. - */ - if (mips_machgroup != MACH_GROUP_JAZZ) + for (i = 0; sonic_portlist[i].port; i++) { + int base_addr = sonic_portlist[i].port; + if (check_region(base_addr, 0x100)) + continue; + if (sonic_probe1(dev, base_addr, sonic_portlist[i].irq) == 0) + return 0; + } return -ENODEV; - if (base_addr >= KSEG0) /* Check a single specified location. */ - return sonic_probe1(dev, base_addr, dev->irq); - else if (base_addr != 0) /* Don't probe at all. */ - return -ENXIO; - - for (i = 0; sonic_portlist[i].port; i++) { - int base_addr = sonic_portlist[i].port; - if (check_region(base_addr, 0x100)) - continue; - if (sonic_probe1(dev, base_addr, sonic_portlist[i].irq) == 0) - return 0; - } - return -ENODEV; } -static int __init sonic_probe1(struct net_device *dev, - unsigned int base_addr, unsigned int irq) +static int __init sonic_probe1(struct net_device *dev, unsigned int base_addr, + unsigned int irq) { - static unsigned version_printed; - unsigned int silicon_revision; - unsigned int val; - struct sonic_local *lp; - int i; - - /* - * get the Silicon Revision ID. If this is one of the known - * one assume that we found a SONIC ethernet controller at - * the expected location. - */ - silicon_revision = SONIC_READ(SONIC_SR); - if (sonic_debug > 1) - printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision); - - i = 0; - while ((known_revisions[i] != 0xffff) && - (known_revisions[i] != silicon_revision)) - i++; - - if (known_revisions[i] == 0xffff) { - printk("SONIC ethernet controller not found (0x%4x)\n", - silicon_revision); - return -ENODEV; - } - - if (!request_region(base_addr, 0x100, dev->name)) - return -EBUSY; - - if (sonic_debug && version_printed++ == 0) - printk(version); - - printk("%s: %s found at 0x%08x, ", - dev->name, "SONIC ethernet", base_addr); - - /* Fill in the 'dev' fields. */ - dev->base_addr = base_addr; - dev->irq = irq; - - /* - * Put the sonic into software reset, then - * retrieve and print the ethernet address. - */ - SONIC_WRITE(SONIC_CMD,SONIC_CR_RST); - SONIC_WRITE(SONIC_CEP,0); - for (i=0; i<3; i++) { - val = SONIC_READ(SONIC_CAP0-i); - dev->dev_addr[i*2] = val; - dev->dev_addr[i*2+1] = val >> 8; - } - - printk("HW Address "); - for (i = 0; i < 6; i++) { - printk("%2.2x", dev->dev_addr[i]); - if (i<5) - printk(":"); - } - - printk(" IRQ %d\n", irq); - - /* Initialize the device structure. */ - if (dev->priv == NULL) { + static unsigned version_printed; + unsigned int silicon_revision; + unsigned int val; + struct sonic_local *lp; + int i; + /* - * the memory be located in the same 64kb segment + * get the Silicon Revision ID. If this is one of the known + * one assume that we found a SONIC ethernet controller at + * the expected location. */ - lp = NULL; + silicon_revision = SONIC_READ(SONIC_SR); + if (sonic_debug > 1) + printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision); + i = 0; - do { - lp = (struct sonic_local *)kmalloc(sizeof(*lp), GFP_KERNEL); - if ((unsigned long)lp >> 16 != ((unsigned long)lp + sizeof(*lp) ) >> 16) { - /* FIXME, free the memory later */ - kfree (lp); - lp = NULL; - } - } while (lp == NULL && i++ < 20); - - if (lp == NULL) { - printk ("%s: couldn't allocate memory for descriptors\n", - dev->name); - return -ENOMEM; + while (known_revisions[i] != 0xffff + && known_revisions[i] != silicon_revision) + i++; + + if (known_revisions[i] == 0xffff) { + printk("SONIC ethernet controller not found (0x%4x)\n", + silicon_revision); + return -ENODEV; } - - memset(lp, 0, sizeof(struct sonic_local)); - - /* get the virtual dma address */ - lp->cda_laddr = vdma_alloc(PHYSADDR(lp),sizeof(*lp)); - if (lp->cda_laddr == ~0UL) { - printk ("%s: couldn't get DMA page entry for descriptors\n", - dev->name); - return -ENOMEM; + + if (!request_region(base_addr, 0x100, dev->name)) + return -EBUSY; + + if (sonic_debug && version_printed++ == 0) + printk(version); + + printk("%s: Sonic ethernet found at 0x%08lx, ", dev->name, base_addr); + + /* Fill in the 'dev' fields. */ + dev->base_addr = base_addr; + dev->irq = irq; + + /* + * Put the sonic into software reset, then + * retrieve and print the ethernet address. + */ + SONIC_WRITE(SONIC_CMD,SONIC_CR_RST); + SONIC_WRITE(SONIC_CEP,0); + for (i=0; i<3; i++) { + val = SONIC_READ(SONIC_CAP0-i); + dev->dev_addr[i*2] = val; + dev->dev_addr[i*2+1] = val >> 8; } - lp->tda_laddr = lp->cda_laddr + sizeof (lp->cda); - lp->rra_laddr = lp->tda_laddr + sizeof (lp->tda); - lp->rda_laddr = lp->rra_laddr + sizeof (lp->rra); - - /* allocate receive buffer area */ - /* FIXME, maybe we should use skbs */ - if ((lp->rba = (char *)kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_KERNEL)) == NULL) { - printk ("%s: couldn't allocate receive buffers\n",dev->name); - return -ENOMEM; + printk("HW Address "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i<5) + printk(":"); } + + printk(" IRQ %d\n", irq); + + /* Initialize the device structure. */ + if (dev->priv == NULL) { + /* + * the memory be located in the same 64kb segment + */ + lp = NULL; + i = 0; + do { + lp = kmalloc(sizeof(*lp), GFP_KERNEL); + if ((unsigned long) lp >> 16 + != ((unsigned long)lp + sizeof(*lp) ) >> 16) { + /* FIXME, free the memory later */ + kfree(lp); + lp = NULL; + } + } while (lp == NULL && i++ < 20); + + if (lp == NULL) { + printk("%s: couldn't allocate memory for descriptors\n", + dev->name); + return -ENOMEM; + } + + memset(lp, 0, sizeof(struct sonic_local)); + + /* get the virtual dma address */ + lp->cda_laddr = vdma_alloc(PHYSADDR(lp),sizeof(*lp)); + if (lp->cda_laddr == ~0UL) { + printk("%s: couldn't get DMA page entry for " + "descriptors\n", dev->name); + return -ENOMEM; + } + + lp->tda_laddr = lp->cda_laddr + sizeof (lp->cda); + lp->rra_laddr = lp->tda_laddr + sizeof (lp->tda); + lp->rda_laddr = lp->rra_laddr + sizeof (lp->rra); - /* get virtual dma address */ - if ((lp->rba_laddr = vdma_alloc(PHYSADDR(lp->rba),SONIC_NUM_RRS * SONIC_RBSIZE)) == ~0UL) { - printk ("%s: couldn't get DMA page entry for receive buffers\n",dev->name); - return -ENOMEM; + /* allocate receive buffer area */ + /* FIXME, maybe we should use skbs */ + lp->rba = kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_KERNEL); + if (!lp->rba) { + printk("%s: couldn't allocate receive buffers\n", + dev->name); + return -ENOMEM; + } + + /* get virtual dma address */ + lp->rba_laddr = vdma_alloc(PHYSADDR(lp->rba), + SONIC_NUM_RRS * SONIC_RBSIZE); + if (lp->rba_laddr == ~0UL) { + printk("%s: couldn't get DMA page entry for receive " + "buffers\n",dev->name); + return -ENOMEM; + } + + /* now convert pointer to KSEG1 pointer */ + lp->rba = (char *)KSEG1ADDR(lp->rba); + flush_cache_all(); + dev->priv = (struct sonic_local *)KSEG1ADDR(lp); } - - /* now convert pointer to KSEG1 pointer */ - lp->rba = (char *)KSEG1ADDR(lp->rba); - flush_cache_all(); - dev->priv = (struct sonic_local *)KSEG1ADDR(lp); - } - - lp = (struct sonic_local *)dev->priv; - dev->open = sonic_open; - dev->stop = sonic_close; - dev->hard_start_xmit = sonic_send_packet; - dev->get_stats = sonic_get_stats; - dev->set_multicast_list = &sonic_multicast_list; - - /* - * clear tally counter - */ - SONIC_WRITE(SONIC_CRCT,0xffff); - SONIC_WRITE(SONIC_FAET,0xffff); - SONIC_WRITE(SONIC_MPT,0xffff); - - /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); - return 0; + + lp = (struct sonic_local *)dev->priv; + dev->open = sonic_open; + dev->stop = sonic_close; + dev->hard_start_xmit = sonic_send_packet; + dev->get_stats = sonic_get_stats; + dev->set_multicast_list = &sonic_multicast_list; + dev->watchdog_timeo = TX_TIMEOUT; + + /* + * clear tally counter + */ + SONIC_WRITE(SONIC_CRCT,0xffff); + SONIC_WRITE(SONIC_FAET,0xffff); + SONIC_WRITE(SONIC_MPT,0xffff); + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + return 0; } /* diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c index ccb9748cc463..0255fbeaf4ac 100644 --- a/drivers/net/lp486e.c +++ b/drivers/net/lp486e.c @@ -7,7 +7,7 @@ Written 1993 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may only be used and - distributed according to the terms of the GNU Public License + distributed according to the terms of the GNU General Public License as modified by SRC, incorporated herein by reference. The author may be reached as becker@scyld.com, or C/O @@ -67,7 +67,7 @@ All other communication is through memory! #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c new file mode 100644 index 000000000000..2eb810c808d7 --- /dev/null +++ b/drivers/net/ns83820.c @@ -0,0 +1,1429 @@ +#define VERSION "0.11" +/* ns83820.c by Benjamin LaHaise <bcrl@redhat.com> + * + * $Revision: 1.34.2.2 $ + * + * Copyright 2001 Benjamin LaHaise. + * Copyright 2001 Red Hat. + * + * Mmmm, chocolate vanilla mocha... + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * ChangeLog + * ========= + * 20010414 0.1 - created + * 20010622 0.2 - basic rx and tx. + * 20010711 0.3 - added duplex and link state detection support. + * 20010713 0.4 - zero copy, no hangs. + * 0.5 - 64 bit dma support (davem will hate me for this) + * - disable jumbo frames to avoid tx hangs + * - work around tx deadlocks on my 1.02 card via + * fiddling with TXCFG + * 20010810 0.6 - use pci dma api for ringbuffers, work on ia64 + * 20010816 0.7 - misc cleanups + * 20010826 0.8 - fix critical zero copy bugs + * 0.9 - internal experiment + * 20010827 0.10 - fix ia64 unaligned access. + * 20010906 0.11 - accept all packets with checksum errors as + * otherwise fragments get lost + - fix >> 32 bugs + * + * Driver Overview + * =============== + * + * This driver was originally written for the National Semiconductor + * 83820 chip, a 10/100/1000 Mbps 64 bit PCI ethernet NIC. Hopefully + * this code will turn out to be a) clean, b) correct, and c) fast. + * With that in mind, I'm aiming to split the code up as much as + * reasonably possible. At present there are X major sections that + * break down into a) packet receive, b) packet transmit, c) link + * management, d) initialization and configuration. Where possible, + * these code paths are designed to run in parallel. + * + * This driver has been tested and found to work with the following + * cards (in no particular order): + * + * Cameo SOHO-GA2000T SOHO-GA2500T + * D-Link DGE-500T + * PureData PDP8023Z-TG + * SMC SMC9462TX + * + * Reports of success or failure would be greatly appreciated. + */ +//#define dprintk printk +#define dprintk(x...) do { } while (0) + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> +#include <linux/smp_lock.h> +#include <linux/tqueue.h> +#include <linux/init.h> +#include <linux/ip.h> /* for iph */ +#include <linux/in.h> /* for IPPROTO_... */ +#include <linux/eeprom.h> +//#include <linux/skbrefill.h> + +/* Dprintk is used for more interesting debug events */ +#undef Dprintk +#define Dprintk dprintk + +#if !defined(GCC_VERSION) || (GCC_VERSION < 2096) +#define __builtin_expect(x,y) (x) +#endif + +#ifdef CONFIG_HIGHMEM64G +#define USE_64BIT_ADDR +#elif defined(__ia64__) +#define USE_64BIT_ADDR +#endif + +/* Tell davem to fix the pci dma api. Grrr. */ +/* stolen from acenic.c */ +#ifdef CONFIG_HIGHMEM +#if defined(CONFIG_X86) +#define DMAADDR_OFFSET 0 +#if defined(CONFIG_HIGHMEM64G) +typedef u64 dmaaddr_high_t; +#else +typedef u32 dmaaddr_high_t; +#endif +#elif defined(CONFIG_PPC) +#define DMAADDR_OFFSET PCI_DRAM_OFFSET +typedef unsigned long dmaaddr_high_t; +#endif + +static inline dmaaddr_high_t +pci_map_single_high(struct pci_dev *hwdev, struct page *page, + int offset, size_t size, int dir) +{ + u64 phys; + phys = page - mem_map; + phys <<= PAGE_SHIFT; + phys += offset; + phys += DMAADDR_OFFSET; + return phys; +} +#else + +typedef unsigned long dmaaddr_high_t; + +static inline dmaaddr_high_t +pci_map_single_high(struct pci_dev *hwdev, struct page *page, + int offset, size_t size, int dir) +{ + return pci_map_single(hwdev, page_address(page) + offset, size, dir); +} +#endif + +/* tunables */ +#define RX_BUF_SIZE 6144 /* 8192 */ +#define NR_RX_DESC 256 + +#define NR_TX_DESC 256 + +/* register defines */ +#define CFGCS 0x04 + +#define CR_TXE 0x00000001 +#define CR_TXD 0x00000002 +#define CR_RXE 0x00000004 +#define CR_RXD 0x00000008 +#define CR_TXR 0x00000010 +#define CR_RXR 0x00000020 +#define CR_SWI 0x00000080 +#define CR_RST 0x00000100 + +#define PTSCR_EEBIST_EN 0x00000002 +#define PTSCR_EELOAD_EN 0x00000004 + +#define ISR_TXDESC3 0x40000000 +#define ISR_TXDESC2 0x20000000 +#define ISR_TXDESC1 0x10000000 +#define ISR_TXDESC0 0x08000000 +#define ISR_RXDESC3 0x04000000 +#define ISR_RXDESC2 0x02000000 +#define ISR_RXDESC1 0x01000000 +#define ISR_RXDESC0 0x00800000 +#define ISR_TXRCMP 0x00400000 +#define ISR_RXRCMP 0x00200000 +#define ISR_DPERR 0x00100000 +#define ISR_SSERR 0x00080000 +#define ISR_RMABT 0x00040000 +#define ISR_RTABT 0x00020000 +#define ISR_RXSOVR 0x00010000 +#define ISR_HIBINT 0x00008000 +#define ISR_PHY 0x00004000 +#define ISR_PME 0x00002000 +#define ISR_SWI 0x00001000 +#define ISR_MIB 0x00000800 +#define ISR_TXURN 0x00000400 +#define ISR_TXIDLE 0x00000200 +#define ISR_TXERR 0x00000100 +#define ISR_TXDESC 0x00000080 +#define ISR_TXOK 0x00000040 +#define ISR_RXORN 0x00000020 +#define ISR_RXIDLE 0x00000010 +#define ISR_RXEARLY 0x00000008 +#define ISR_RXERR 0x00000004 +#define ISR_RXDESC 0x00000002 +#define ISR_RXOK 0x00000001 + +#define TXCFG_CSI 0x80000000 +#define TXCFG_HBI 0x40000000 +#define TXCFG_MLB 0x20000000 +#define TXCFG_ATP 0x10000000 +#define TXCFG_ECRETRY 0x00800000 +#define TXCFG_BRST_DIS 0x00080000 +#define TXCFG_MXDMA1024 0x00000000 +#define TXCFG_MXDMA512 0x00700000 +#define TXCFG_MXDMA256 0x00600000 +#define TXCFG_MXDMA128 0x00500000 +#define TXCFG_MXDMA64 0x00400000 +#define TXCFG_MXDMA32 0x00300000 +#define TXCFG_MXDMA16 0x00200000 +#define TXCFG_MXDMA8 0x00100000 + +#define CFG_LNKSTS 0x80000000 +#define CFG_SPDSTS 0x60000000 +#define CFG_SPDSTS1 0x40000000 +#define CFG_SPDSTS0 0x20000000 +#define CFG_DUPSTS 0x10000000 +#define CFG_TBI_EN 0x01000000 +#define CFG_MODE_1000 0x00400000 +#define CFG_PINT_CTL 0x001c0000 +#define CFG_PINT_DUPSTS 0x00100000 +#define CFG_PINT_LNKSTS 0x00080000 +#define CFG_PINT_SPDSTS 0x00040000 +#define CFG_TMRTEST 0x00020000 +#define CFG_MRM_DIS 0x00010000 +#define CFG_MWI_DIS 0x00008000 +#define CFG_T64ADDR 0x00004000 +#define CFG_PCI64_DET 0x00002000 +#define CFG_DATA64_EN 0x00001000 +#define CFG_M64ADDR 0x00000800 +#define CFG_PHY_RST 0x00000400 +#define CFG_PHY_DIS 0x00000200 +#define CFG_EXTSTS_EN 0x00000100 +#define CFG_REQALG 0x00000080 +#define CFG_SB 0x00000040 +#define CFG_POW 0x00000020 +#define CFG_EXD 0x00000010 +#define CFG_PESEL 0x00000008 +#define CFG_BROM_DIS 0x00000004 +#define CFG_EXT_125 0x00000002 +#define CFG_BEM 0x00000001 + +#define EXTSTS_UDPPKT 0x00200000 +#define EXTSTS_TCPPKT 0x00080000 +#define EXTSTS_IPPKT 0x00020000 + +#define SPDSTS_POLARITY (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS) + +#define MIBC_MIBS 0x00000008 +#define MIBC_ACLR 0x00000004 +#define MIBC_FRZ 0x00000002 +#define MIBC_WRN 0x00000001 + +#define RXCFG_AEP 0x80000000 +#define RXCFG_ARP 0x40000000 +#define RXCFG_STRIPCRC 0x20000000 +#define RXCFG_RX_FD 0x10000000 +#define RXCFG_ALP 0x08000000 +#define RXCFG_AIRL 0x04000000 +#define RXCFG_MXDMA 0x00700000 +#define RXCFG_MXDMA0 0x00100000 +#define RXCFG_MXDMA64 0x00600000 +#define RXCFG_DRTH 0x0000003e +#define RXCFG_DRTH0 0x00000002 + +#define RFCR_RFEN 0x80000000 +#define RFCR_AAB 0x40000000 +#define RFCR_AAM 0x20000000 +#define RFCR_AAU 0x10000000 +#define RFCR_APM 0x08000000 +#define RFCR_APAT 0x07800000 +#define RFCR_APAT3 0x04000000 +#define RFCR_APAT2 0x02000000 +#define RFCR_APAT1 0x01000000 +#define RFCR_APAT0 0x00800000 +#define RFCR_AARP 0x00400000 +#define RFCR_MHEN 0x00200000 +#define RFCR_UHEN 0x00100000 +#define RFCR_ULM 0x00080000 + +#define VRCR_RUDPE 0x00000080 +#define VRCR_RTCPE 0x00000040 +#define VRCR_RIPE 0x00000020 +#define VRCR_IPEN 0x00000010 +#define VRCR_DUTF 0x00000008 +#define VRCR_DVTF 0x00000004 +#define VRCR_VTREN 0x00000002 +#define VRCR_VTDEN 0x00000001 + +#define VTCR_PPCHK 0x00000008 +#define VTCR_GCHK 0x00000004 +#define VTCR_VPPTI 0x00000002 +#define VTCR_VGTI 0x00000001 + +#define CR 0x00 +#define CFG 0x04 +#define MEAR 0x08 +#define PTSCR 0x0c +#define ISR 0x10 +#define IMR 0x14 +#define IER 0x18 +#define IHR 0x1c +#define TXDP 0x20 +#define TXDP_HI 0x24 +#define TXCFG 0x28 +#define GPIOR 0x2c +#define RXDP 0x30 +#define RXDP_HI 0x34 +#define RXCFG 0x38 +#define PQCR 0x3c +#define WCSR 0x40 +#define PCR 0x44 +#define RFCR 0x48 +#define RFDR 0x4c + +#define SRR 0x58 + +#define VRCR 0xbc +#define VTCR 0xc0 +#define VDR 0xc4 +#define CCSR 0xcc + +#define __kick_rx(dev) writel(CR_RXE, dev->base + CR) + +#define kick_rx(dev) do { \ + dprintk("kick_rx: maybe kicking\n"); \ + if (test_and_clear_bit(0, &dev->rx_info.idle)) { \ + dprintk("actually kicking\n"); \ + writel(dev->rx_info.phy_descs + (4 * DESC_SIZE * dev->rx_info.next_rx), dev->base + RXDP); \ + if (dev->rx_info.next_rx == dev->rx_info.next_empty) \ + printk(KERN_DEBUG "%s: uh-oh: next_rx == next_empty???\n", dev->net_dev.name);\ + __kick_rx(dev); \ + } \ +} while(0) + +#ifdef USE_64BIT_ADDR +typedef u64 hw_addr_t; +#else +typedef u32 hw_addr_t; +#endif + +#define HW_ADDR_LEN (sizeof(hw_addr_t)) + +#define LINK 0 +#define BUFPTR (LINK + HW_ADDR_LEN/4) +#define CMDSTS (BUFPTR + HW_ADDR_LEN/4) +#define EXTSTS (CMDSTS + 4/4) +#define DRV_NEXT (EXTSTS + 4/4) + +#define CMDSTS_OWN 0x80000000 +#define CMDSTS_MORE 0x40000000 +#define CMDSTS_INTR 0x20000000 +#define CMDSTS_ERR 0x10000000 +#define CMDSTS_OK 0x08000000 + +#define CMDSTS_DEST_MASK 0x01800000 +#define CMDSTS_DEST_SELF 0x00800000 +#define CMDSTS_DEST_MULTI 0x01000000 + +#define DESC_SIZE 8 /* Should be cache line sized */ + +struct rx_info { + spinlock_t lock; + int up; + long idle; + + struct sk_buff *skbs[NR_RX_DESC]; + + unsigned next_rx, next_empty; + + u32 *descs; + dma_addr_t phy_descs; +}; + + +struct ns83820 { + struct net_device net_dev; + u8 *base; + + struct pci_dev *pci_dev; + struct ns83820 *next_dev; + + struct rx_info rx_info; + + unsigned ihr; + struct tq_struct tq_refill; + + /* protects everything below. irqsave when using. */ + spinlock_t misc_lock; + + u32 CFG_cache; + + u32 MEAR_cache; + u32 IMR_cache; + struct eeprom ee; + + + spinlock_t tx_lock; + + long tx_idle; + u32 tx_done_idx; + u32 tx_idx; + volatile u32 tx_free_idx; /* idx of free desc chain */ + u32 tx_intr_idx; + + struct sk_buff *tx_skbs[NR_TX_DESC]; + + char pad[16] __attribute__((aligned(16))); + u32 *tx_descs; + dma_addr_t tx_phy_descs; +}; + +//free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC +#define start_tx_okay(dev) \ + (((NR_TX_DESC-2 + dev->tx_done_idx - dev->tx_free_idx) % NR_TX_DESC) > NR_TX_DESC/2) + +static struct ns83820 *ns83820_chain; + + +/* Packet Receiver + * + * The hardware supports linked lists of receive descriptors for + * which ownership is transfered back and forth by means of an + * ownership bit. While the hardware does support the use of a + * ring for receive descriptors, we only make use of a chain in + * an attempt to reduce bus traffic under heavy load scenarios. + * This will also make bugs a bit more obvious. The current code + * only makes use of a single rx chain; I hope to implement + * priority based rx for version 1.0. Goal: even under overload + * conditions, still route realtime traffic with as low jitter as + * possible. + */ +#ifdef USE_64BIT_ADDR +static inline void build_rx_desc64(struct ns83820 *dev, u32 *desc, u64 link, u64 buf, u32 cmdsts, u32 extsts) +{ + desc[0] = link; + desc[1] = link >> 32; + desc[2] = buf; + desc[3] = buf >> 32; + desc[5] = extsts; + mb(); + desc[4] = cmdsts; +} + +#define build_rx_desc build_rx_desc64 +#else + +static inline void build_rx_desc32(struct ns83820 *dev, u32 *desc, u32 link, u32 buf, u32 cmdsts, u32 extsts) +{ + desc[0] = link; + desc[1] = buf; + desc[3] = extsts; + mb(); + desc[2] = cmdsts; +} + +#define build_rx_desc build_rx_desc32 +#endif + +#define nr_rx_empty(dev) ((NR_RX_DESC-2 + dev->rx_info.next_rx - dev->rx_info.next_empty) % NR_RX_DESC) +static inline int ns83820_add_rx_skb(struct ns83820 *dev, struct sk_buff *skb) +{ + unsigned next_empty; + u32 cmdsts; + u32 *sg; + hw_addr_t buf; + + next_empty = dev->rx_info.next_empty; + + /* don't overrun last rx marker */ + if (nr_rx_empty(dev) <= 2) { + kfree_skb(skb); + return 1; + } + +#if 0 + dprintk("next_empty[%d] nr_used[%d] next_rx[%d]\n", + dev->rx_info.next_empty, + dev->rx_info.nr_used, + dev->rx_info.next_rx + ); +#endif + + sg = dev->rx_info.descs + (next_empty * DESC_SIZE); + if (dev->rx_info.skbs[next_empty]) + BUG(); + dev->rx_info.skbs[next_empty] = skb; + + dev->rx_info.next_empty = (next_empty + 1) % NR_RX_DESC; + cmdsts = RX_BUF_SIZE | CMDSTS_INTR; + buf = pci_map_single(dev->pci_dev, skb->tail, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + build_rx_desc(dev, sg, 0, buf, cmdsts, 0); + /* update link of previous rx */ + if (next_empty != dev->rx_info.next_rx) + dev->rx_info.descs[((NR_RX_DESC + next_empty - 1) % NR_RX_DESC) * DESC_SIZE] = dev->rx_info.phy_descs + (next_empty * DESC_SIZE * 4); + + return 0; +} + +static int rx_refill(struct ns83820 *dev, int gfp) +{ + unsigned i; + long flags = 0; + + dprintk("rx_refill(%p)\n", dev); + if (gfp == GFP_ATOMIC) + spin_lock_irqsave(&dev->rx_info.lock, flags); + for (i=0; i<NR_RX_DESC; i++) { + struct sk_buff *skb; + long res; + skb = __dev_alloc_skb(RX_BUF_SIZE+16, gfp); + if (!skb) + break; + + res = (long)skb->tail & 0xf; + res = 0x10 - res; + res &= 0xf; + skb_reserve(skb, res); + + skb->dev = &dev->net_dev; + if (gfp != GFP_ATOMIC) + spin_lock_irqsave(&dev->rx_info.lock, flags); + res = ns83820_add_rx_skb(dev, skb); + if (gfp != GFP_ATOMIC) + spin_unlock_irqrestore(&dev->rx_info.lock, flags); + if (res) { + i = 1; + break; + } + } + if (gfp == GFP_ATOMIC) + spin_unlock_irqrestore(&dev->rx_info.lock, flags); + + return i ? 0 : -ENOMEM; +} + +/* REFILL */ +static inline void queue_refill(void *_dev) +{ + struct ns83820 *dev = _dev; + + rx_refill(dev, GFP_KERNEL); + if (dev->rx_info.up) + kick_rx(dev); +} + +static inline void clear_rx_desc(struct ns83820 *dev, unsigned i) +{ + build_rx_desc(dev, dev->rx_info.descs + (DESC_SIZE * i), 0, 0, CMDSTS_OWN, 0); +} + +static void phy_intr(struct ns83820 *dev) +{ + static char *speeds[] = { "10", "100", "1000", "1000(?)" }; + u32 cfg, new_cfg; + + new_cfg = dev->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS); + cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; + + if (cfg & CFG_SPDSTS1) + new_cfg |= CFG_MODE_1000 | CFG_SB; + else + new_cfg &= ~CFG_MODE_1000 | CFG_SB; + + if ((cfg & CFG_LNKSTS) && ((new_cfg ^ dev->CFG_cache) & CFG_MODE_1000)) { + writel(new_cfg, dev->base + CFG); + dev->CFG_cache = new_cfg; + } + + dev->CFG_cache &= ~CFG_SPDSTS; + dev->CFG_cache |= cfg & CFG_SPDSTS; + + if (cfg & CFG_LNKSTS) { + netif_start_queue(&dev->net_dev); + netif_wake_queue(&dev->net_dev); + } else { + netif_stop_queue(&dev->net_dev); + } + + if (cfg & CFG_LNKSTS) + printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n", + dev->net_dev.name, + speeds[((cfg / CFG_SPDSTS0) & 3)], + (cfg & CFG_DUPSTS) ? "full" : "half"); + else + printk(KERN_INFO "%s: link now down.\n", dev->net_dev.name); +} + +static int ns83820_setup_rx(struct ns83820 *dev) +{ + unsigned i; + int ret; + + dprintk("ns83820_setup_rx(%p)\n", dev); + + dev->rx_info.idle = 1; + dev->rx_info.next_rx = 0; + dev->rx_info.next_empty = 0; + + for (i=0; i<NR_RX_DESC; i++) + clear_rx_desc(dev, i); + + writel(0, dev->base + RXDP_HI); + writel(dev->rx_info.phy_descs, dev->base + RXDP); + + ret = rx_refill(dev, GFP_KERNEL); + if (!ret) { + dprintk("starting receiver\n"); + /* prevent the interrupt handler from stomping on us */ + spin_lock_irq(&dev->rx_info.lock); + + writel(0x0001, dev->base + CCSR); + writel(0, dev->base + RFCR); + writel(0x7fc00000, dev->base + RFCR); + writel(0xffc00000, dev->base + RFCR); + + dev->rx_info.up = 1; + + phy_intr(dev); + + /* Okay, let it rip */ + spin_lock(&dev->misc_lock); + dev->IMR_cache |= ISR_PHY; + dev->IMR_cache |= ISR_RXRCMP; + //dev->IMR_cache |= ISR_RXERR; + //dev->IMR_cache |= ISR_RXOK; + dev->IMR_cache |= ISR_RXORN; + dev->IMR_cache |= ISR_RXSOVR; + dev->IMR_cache |= ISR_RXDESC; + dev->IMR_cache |= ISR_RXIDLE; + dev->IMR_cache |= ISR_TXDESC; + //dev->IMR_cache |= ISR_TXIDLE; + + writel(dev->IMR_cache, dev->base + IMR); + writel(1, dev->base + IER); + spin_unlock(&dev->misc_lock); + + kick_rx(dev); + + spin_unlock_irq(&dev->rx_info.lock); + } + return ret; +} + +static void ns83820_cleanup_rx(struct ns83820 *dev) +{ + unsigned i; + long flags; + + dprintk("ns83820_cleanup_rx(%p)\n", dev); + + /* disable receive interrupts */ + spin_lock_irqsave(&dev->misc_lock, flags); + dev->IMR_cache &= ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY | ISR_RXIDLE); + writel(dev->IMR_cache, dev->base + IMR); + spin_unlock_irqrestore(&dev->misc_lock, flags); + + /* synchronize with the interrupt handler and kill it */ + dev->rx_info.up = 0; + synchronize_irq(); + + /* touch the pci bus... */ + readl(dev->base + IMR); + + /* assumes the transmitter is already disabled and reset */ + writel(0, dev->base + RXDP_HI); + writel(0, dev->base + RXDP); + + for (i=0; i<NR_RX_DESC; i++) { + struct sk_buff *skb = dev->rx_info.skbs[i]; + dev->rx_info.skbs[i] = NULL; + clear_rx_desc(dev, i); + if (skb) + kfree_skb(skb); + } +} + +/* rx_irq + * + */ +static void FASTCALL(rx_irq(struct ns83820 *dev)); +static void rx_irq(struct ns83820 *dev) +{ + struct rx_info *info = &dev->rx_info; + unsigned next_rx; + u32 cmdsts, *desc; + long flags; + int nr = 0; + + dprintk("rx_irq(%p)\n", dev); + dprintk("rxdp: %08x, descs: %08lx next_rx[%d]: %p next_empty[%d]: %p\n", + readl(dev->base + RXDP), + (dev->rx_info.phy_descs), + dev->rx_info.next_rx, + (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_rx)), + dev->rx_info.next_empty, + (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_empty)) + ); + + spin_lock_irqsave(&info->lock, flags); + if (!info->up) + goto out; + + dprintk("walking descs\n"); + next_rx = info->next_rx; + desc = info->descs + (DESC_SIZE * next_rx); + while ((CMDSTS_OWN & (cmdsts = desc[CMDSTS])) && + (cmdsts != CMDSTS_OWN)) { + struct sk_buff *skb; + u32 extsts = desc[EXTSTS]; + dmaaddr_high_t bufptr = *(hw_addr_t *)(desc + BUFPTR); + + dprintk("cmdsts: %08x\n", cmdsts); + dprintk("link: %08x\n", desc[LINK]); + dprintk("extsts: %08x\n", desc[EXTSTS]); + + skb = info->skbs[next_rx]; + info->skbs[next_rx] = NULL; + info->next_rx = (next_rx + 1) % NR_RX_DESC; + + barrier(); + clear_rx_desc(dev, next_rx); + + pci_unmap_single(dev->pci_dev, bufptr, + RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + if (CMDSTS_OK & cmdsts) { +#ifndef __i386__ + struct sk_buff *tmp; +#endif + int len = cmdsts & 0xffff; + if (!skb) + BUG(); + skb_put(skb, len); +#ifndef __i386__ /* I hate the network stack sometimes */ + tmp = __dev_alloc_skb(RX_BUF_SIZE+16, GFP_ATOMIC); + if (!tmp) + goto done; + tmp->dev = &dev->net_dev; + skb_reserve(tmp, 2); + memcpy(skb_put(tmp, len), skb->data, len); + kfree_skb(skb); + skb = tmp; +#endif + if ((extsts & 0x002a0000) && !(extsts & 0x00540000)) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + skb->ip_summed = CHECKSUM_NONE; + } + skb->protocol = eth_type_trans(skb, &dev->net_dev); + switch (netif_rx(skb)) { + case NET_RX_SUCCESS: + dev->ihr = 3; + break; + case NET_RX_CN_LOW: + dev->ihr = 3; + break; + case NET_RX_CN_MOD: + dev->ihr = dev->ihr + 1; + break; + case NET_RX_CN_HIGH: + dev->ihr += dev->ihr/2 + 1; + break; + case NET_RX_DROP: + dev->ihr = 255; + break; + } + if (dev->ihr > 255) + dev->ihr = 255; +#ifndef __i386__ + done:; +#endif + } else { + static int err; + if (err++ < 20) { + Dprintk("error packet: cmdsts: %08x extsts: %08x\n", cmdsts, extsts); + } + kfree_skb(skb); + } + + nr++; + next_rx = info->next_rx; + desc = info->descs + (DESC_SIZE * next_rx); + } + info->next_rx = next_rx; + +out: + if (0 && !nr) { + Dprintk("dazed: cmdsts_f: %08x\n", cmdsts); + } + + spin_unlock_irqrestore(&info->lock, flags); +} + + +/* Packet Transmit code + */ +static inline void kick_tx(struct ns83820 *dev) +{ + dprintk("kick_tx(%p): tx_idle=%ld, tx_idx=%d free_idx=%d\n", + dev, dev->tx_idle, dev->tx_idx, dev->tx_free_idx); + writel(CR_TXE, dev->base + CR); +} + +/* no spinlock needed on the transmit irq path as the interrupt handler is serialized */ +static void do_tx_done(struct ns83820 *dev) +{ + u32 cmdsts, tx_done_idx, *desc; + + dprintk("do_tx_done(%p)\n", dev); + tx_done_idx = dev->tx_done_idx; + desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); + + dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n", + tx_done_idx, dev->tx_free_idx, desc[CMDSTS]); + while ((tx_done_idx != dev->tx_free_idx) && + !(CMDSTS_OWN & (cmdsts = desc[CMDSTS])) ) { + struct sk_buff *skb; + + dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n", + tx_done_idx, dev->tx_free_idx, desc[CMDSTS]); + skb = dev->tx_skbs[tx_done_idx]; + dev->tx_skbs[tx_done_idx] = NULL; + dprintk("done(%p)\n", skb); + if (skb) { + pci_unmap_single(dev->pci_dev, + *(hw_addr_t *)(desc + BUFPTR), + skb->len, + PCI_DMA_TODEVICE); + dev_kfree_skb_irq(skb); + } + + tx_done_idx = (tx_done_idx + 1) % NR_TX_DESC; + dev->tx_done_idx = tx_done_idx; + desc[CMDSTS] = 0; + barrier(); + desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); + } + + /* Allow network stack to resume queueing packets after we've + * finished transmitting at least 1/4 of the packets in the queue. + */ + if (netif_queue_stopped(&dev->net_dev) && start_tx_okay(dev)) { + dprintk("start_queue(%p)\n", dev); + netif_start_queue(&dev->net_dev); + netif_wake_queue(&dev->net_dev); + } +} + +static void ns83820_cleanup_tx(struct ns83820 *dev) +{ + unsigned i; + + for (i=0; i<NR_TX_DESC; i++) { + struct sk_buff *skb = dev->tx_skbs[i]; + dev->tx_skbs[i] = NULL; + if (skb) + dev_kfree_skb(skb); + } + + memset(dev->tx_descs, 0, NR_TX_DESC * DESC_SIZE * 4); + set_bit(0, &dev->tx_idle); +} + +/* transmit routine. This code relies on the network layer serializing + * its calls in, but will run happily in parallel with the interrupt + * handler. This code currently has provisions for fragmenting tx buffers + * while trying to track down a bug in either the zero copy code or + * the tx fifo (hence the MAX_FRAG_LEN). + */ +#define MAX_FRAG_LEN 8192 /* disabled for now */ +static int ns83820_hard_start_xmit(struct sk_buff *skb, struct net_device *_dev) +{ + struct ns83820 *dev = (struct ns83820 *)_dev; + u32 free_idx, cmdsts, extsts; + int nr_free, nr_frags; + unsigned tx_done_idx; + dmaaddr_high_t buf; + unsigned len; + skb_frag_t *frag; + int stopped = 0; + int do_intr = 0; + volatile u32 *first_desc; + + dprintk("ns83820_hard_start_xmit\n"); + + nr_frags = skb_shinfo(skb)->nr_frags; +again: + if (__builtin_expect(dev->CFG_cache & CFG_LNKSTS, 0)) { + netif_stop_queue(&dev->net_dev); + if (__builtin_expect(dev->CFG_cache & CFG_LNKSTS, 0)) + return 1; + netif_start_queue(&dev->net_dev); + } + + free_idx = dev->tx_free_idx; + tx_done_idx = dev->tx_done_idx; + nr_free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC; + nr_free -= 1; + if ((nr_free <= nr_frags) || (nr_free <= 8192 / MAX_FRAG_LEN)) { + dprintk("stop_queue - not enough(%p)\n", dev); + netif_stop_queue(&dev->net_dev); + + /* Check again: we may have raced with a tx done irq */ + if (dev->tx_done_idx != tx_done_idx) { + dprintk("restart queue(%p)\n", dev); + netif_start_queue(&dev->net_dev); + goto again; + } + return 1; + } + + if (free_idx == dev->tx_intr_idx) { + do_intr = 1; + dev->tx_intr_idx = (dev->tx_intr_idx + NR_TX_DESC/2) % NR_TX_DESC; + } + + nr_free -= nr_frags; + if (nr_free < 1) { + dprintk("stop_queue - last entry(%p)\n", dev); + netif_stop_queue(&dev->net_dev); + stopped = 1; + } + + frag = skb_shinfo(skb)->frags; + if (!nr_frags) + frag = 0; + extsts = 0; + if (skb->ip_summed == CHECKSUM_HW) { + extsts |= EXTSTS_IPPKT; + if (IPPROTO_TCP == skb->nh.iph->protocol) + extsts |= EXTSTS_TCPPKT; + else if (IPPROTO_UDP == skb->nh.iph->protocol) + extsts |= EXTSTS_UDPPKT; + } + + len = skb->len; + if (nr_frags) + len -= skb->data_len; + buf = pci_map_single(dev->pci_dev, skb->data, len, PCI_DMA_TODEVICE); + + first_desc = dev->tx_descs + (free_idx * DESC_SIZE); + + for (;;) { + volatile u32 *desc = dev->tx_descs + (free_idx * DESC_SIZE); + u32 residue = 0; +#if 0 + if (len > MAX_FRAG_LEN) { + residue = len; + /* align the start address of the next fragment */ + len = MAX_FRAG_LEN; + residue -= len; + } +#endif + + dprintk("frag[%3u]: %4u @ 0x%x%08Lx\n", free_idx, len, + (unsigned long long)buf); + free_idx = (free_idx + 1) % NR_TX_DESC; + desc[LINK] = dev->tx_phy_descs + (free_idx * DESC_SIZE * 4); + *(hw_addr_t *)(desc + BUFPTR) = buf; + desc[EXTSTS] = extsts; + + cmdsts = ((nr_frags|residue) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0); + cmdsts |= (desc == first_desc) ? 0 : CMDSTS_OWN; + cmdsts |= len; + desc[CMDSTS] = cmdsts; + + if (residue) { + buf += len; + len = residue; + continue; + } + + if (!nr_frags) + break; + + buf = pci_map_single_high(dev->pci_dev, frag->page, 0, + frag->size, PCI_DMA_TODEVICE); + dprintk("frag: buf=%08Lx page=%08lx\n", + (long long)buf, (long)(frag->page - mem_map)); + len = frag->size; + frag++; + nr_frags--; + } + dprintk("done pkt\n"); + dev->tx_skbs[free_idx] = skb; + first_desc[CMDSTS] |= CMDSTS_OWN; + dev->tx_free_idx = free_idx; + kick_tx(dev); + + /* Check again: we may have raced with a tx done irq */ + if (stopped && (dev->tx_done_idx != tx_done_idx) && start_tx_okay(dev)) + netif_start_queue(&dev->net_dev); + + return 0; +} + +static void ns83820_irq(int foo, void *data, struct pt_regs *regs) +{ + struct ns83820 *dev = data; + int count = 0; + u32 isr; + dprintk("ns83820_irq(%p)\n", dev); + + dev->ihr = 0; + + while (count++ < 32 && (isr = readl(dev->base + ISR))) { + dprintk("irq: %08x\n", isr); + + if (isr & ~(ISR_PHY | ISR_RXDESC | ISR_RXEARLY | ISR_RXOK | ISR_RXERR | ISR_TXIDLE | ISR_TXOK | ISR_TXDESC)) + Dprintk("odd isr? 0x%08x\n", isr); + + if ((ISR_RXEARLY | ISR_RXIDLE | ISR_RXORN | ISR_RXDESC | ISR_RXOK | ISR_RXERR) & isr) { + if (ISR_RXIDLE & isr) { + dev->rx_info.idle = 1; + Dprintk("oh dear, we are idle\n"); + } + + if ((ISR_RXDESC) & isr) { + rx_irq(dev); + writel(4, dev->base + IHR); + } + + if (nr_rx_empty(dev) >= NR_RX_DESC/4) { + if (dev->rx_info.up) { + rx_refill(dev, GFP_ATOMIC); + kick_rx(dev); + } + } + + if (dev->rx_info.up && nr_rx_empty(dev) > NR_RX_DESC*3/4) + schedule_task(&dev->tq_refill); + else + kick_rx(dev); + if (dev->rx_info.idle) + Dprintk("BAD\n"); + } + + if (ISR_RXSOVR & isr) + Dprintk("overrun\n"); + if (ISR_RXORN & isr) + Dprintk("overrun\n"); + + if ((ISR_RXRCMP & isr) && dev->rx_info.up) + writel(CR_RXE, dev->base + CR); + + if (ISR_TXIDLE & isr) { + u32 txdp; + txdp = readl(dev->base + TXDP); + dprintk("txdp: %08x\n", txdp); + txdp -= dev->tx_phy_descs; + dev->tx_idx = txdp / (DESC_SIZE * 4); + if (dev->tx_idx >= NR_TX_DESC) { + printk(KERN_ALERT "%s: BUG -- txdp out of range\n", dev->net_dev.name); + dev->tx_idx = 0; + } + if (dev->tx_idx != dev->tx_free_idx) + writel(CR_TXE, dev->base + CR); + //kick_tx(dev); + else + dev->tx_idle = 1; + mb(); + if (dev->tx_idx != dev->tx_free_idx) + kick_tx(dev); + } + + /* Defer tx ring processing until more than a minimum amount of + * work has accumulated + */ + if ((ISR_TXDESC | ISR_TXIDLE) & isr) + do_tx_done(dev); + + if (ISR_PHY & isr) + phy_intr(dev); + } + +#if 0 /* Still working on the interrupt mitigation strategy */ + if (dev->ihr) + writel(dev->ihr, dev->base + IHR); +#endif +} + +static void ns83820_do_reset(struct ns83820 *dev, u32 which) +{ + Dprintk("resetting chip...\n"); + writel(which, dev->base + CR); + do { + schedule(); + } while (readl(dev->base + CR) & which); + Dprintk("okay!\n"); +} + +static int ns83820_stop(struct net_device *_dev) +{ + struct ns83820 *dev = (struct ns83820 *)_dev; + + /* FIXME: protect against interrupt handler? */ + + /* disable interrupts */ + writel(0, dev->base + IMR); + writel(0, dev->base + IER); + readl(dev->base + IER); + + dev->rx_info.up = 0; + synchronize_irq(); + + ns83820_do_reset(dev, CR_RST); + + synchronize_irq(); + + dev->IMR_cache &= ~(ISR_TXURN | ISR_TXIDLE | ISR_TXERR | ISR_TXDESC | ISR_TXOK); + ns83820_cleanup_rx(dev); + ns83820_cleanup_tx(dev); + + return 0; +} + +static int ns83820_open(struct net_device *_dev) +{ + struct ns83820 *dev = (struct ns83820 *)_dev; + unsigned i; + u32 desc; + int ret; + + dprintk("ns83820_open\n"); + + writel(0, dev->base + PQCR); + + ret = ns83820_setup_rx(dev); + if (ret) + goto failed; + + memset(dev->tx_descs, 0, 4 * NR_TX_DESC * DESC_SIZE); + for (i=0; i<NR_TX_DESC; i++) { + *(hw_addr_t *)(dev->tx_descs + (i * DESC_SIZE) + LINK) + = dev->tx_phy_descs + + ((i+1) % NR_TX_DESC) * DESC_SIZE * 4; + } + + dev->tx_idx = 0; + dev->tx_done_idx = 0; + desc = dev->tx_phy_descs; + writel(0, dev->base + TXDP_HI); + writel(desc, dev->base + TXDP); + +//printk("IMR: %08x / %08x\n", readl(dev->base + IMR), dev->IMR_cache); + + set_bit(0, &dev->tx_idle); + netif_start_queue(&dev->net_dev); /* FIXME: wait for phy to come up */ + + return 0; + +failed: + ns83820_stop(_dev); + return ret; +} + +#if 0 /* FIXME: implement this! */ +static void ns83820_tx_timeout(struct net_device *_dev) +{ + struct ns83820 *dev = (struct ns83820 *)_dev; + + printk("ns83820_tx_timeout\n"); +} +#endif + +static void ns83820_getmac(struct ns83820 *dev, u8 *mac) +{ + unsigned i; + for (i=0; i<3; i++) { + u32 data; +#if 0 /* I've left this in as an example of how to use eeprom.h */ + data = eeprom_readw(&dev->ee, 0xa + 2 - i); +#else + writel(i*2, dev->base + RFCR); + data = readl(dev->base + RFDR); +#endif + *mac++ = data; + *mac++ = data >> 8; + } +} + +static int ns83820_change_mtu(struct net_device *_dev, int new_mtu) +{ + if (new_mtu > RX_BUF_SIZE) + return -EINVAL; + _dev->mtu = new_mtu; + return 0; +} + +static int ns83820_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +{ + struct ns83820 *dev; + long addr; + int err; + + dev = (struct ns83820 *)alloc_etherdev((sizeof *dev) - (sizeof dev->net_dev)); + err = -ENOMEM; + if (!dev) + goto out; + + spin_lock_init(&dev->rx_info.lock); + spin_lock_init(&dev->tx_lock); + spin_lock_init(&dev->misc_lock); + dev->pci_dev = pci_dev; + + dev->ee.cache = &dev->MEAR_cache; + dev->ee.lock = &dev->misc_lock; + dev->net_dev.owner = THIS_MODULE; + + PREPARE_TQUEUE(&dev->tq_refill, queue_refill, dev); + + err = pci_enable_device(pci_dev); + if (err) { + printk(KERN_INFO "ns83820: pci_enable_dev: %d\n", err); + goto out_free; + } + + pci_set_master(pci_dev); + addr = pci_resource_start(pci_dev, 1); + dev->base = ioremap_nocache(addr, PAGE_SIZE); + dev->tx_descs = pci_alloc_consistent(pci_dev, + 4 * DESC_SIZE * NR_TX_DESC, &dev->tx_phy_descs); + dev->rx_info.descs = pci_alloc_consistent(pci_dev, + 4 * DESC_SIZE * NR_RX_DESC, &dev->rx_info.phy_descs); + err = -ENOMEM; + if (!dev->base || !dev->tx_descs || !dev->rx_info.descs) + goto out_disable; + + dprintk("%p: %08lx %p: %08lx\n", dev->tx_descs, dev->tx_phy_descs, + dev->rx_info.descs, dev->rx_info.phy_descs); + /* disable interrupts */ + writel(0, dev->base + IMR); + writel(0, dev->base + IER); + readl(dev->base + IER); + + dev->IMR_cache = 0; + + setup_ee_mem_bitbanger(&dev->ee, (long)dev->base + MEAR, 3, 2, 1, 0, + 0); + + err = request_irq(pci_dev->irq, ns83820_irq, SA_SHIRQ, + dev->net_dev.name, dev); + if (err) { + printk(KERN_INFO "ns83820: unable to register irq %d\n", + pci_dev->irq); + goto out_unmap; + } + + dev->net_dev.open = ns83820_open; + dev->net_dev.stop = ns83820_stop; + dev->net_dev.hard_start_xmit = ns83820_hard_start_xmit; + dev->net_dev.change_mtu = ns83820_change_mtu; + //FIXME: dev->net_dev.tx_timeout = ns83820_tx_timeout; + + lock_kernel(); + dev->next_dev = ns83820_chain; + ns83820_chain = dev; + unlock_kernel(); + + ns83820_do_reset(dev, CR_RST); + + dprintk("start bist\n"); + writel(PTSCR_EEBIST_EN, dev->base + PTSCR); + do { + schedule(); + } while (readl(dev->base + PTSCR) & PTSCR_EEBIST_EN); + dprintk("done bist\n"); + + dprintk("start eeload\n"); + writel(PTSCR_EELOAD_EN, dev->base + PTSCR); + do { + schedule(); + } while (readl(dev->base + PTSCR) & PTSCR_EELOAD_EN); + dprintk("done eeload\n"); + + /* I love config registers */ + dev->CFG_cache = readl(dev->base + CFG); + + if ((dev->CFG_cache & CFG_PCI64_DET)) { + printk("%s: enabling 64 bit PCI.\n", dev->net_dev.name); + dev->CFG_cache |= CFG_T64ADDR | CFG_DATA64_EN; + } else { + printk("%s: disabling 64 bit PCI.\n", dev->net_dev.name); + dev->CFG_cache &= ~(CFG_T64ADDR | CFG_DATA64_EN); + } + dev->CFG_cache &= (CFG_TBI_EN | CFG_MRM_DIS | CFG_MWI_DIS | + CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 | + CFG_M64ADDR); + dev->CFG_cache |= CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS | + CFG_EXTSTS_EN | CFG_EXD | CFG_PESEL; + dev->CFG_cache |= CFG_REQALG; + dev->CFG_cache |= CFG_POW; +#ifdef USE_64BIT_ADDR + dev->CFG_cache |= CFG_M64ADDR; + printk("using 64 bit addressing\n"); +#endif +#ifdef __LITTLE_ENDIAN + dev->CFG_cache &= ~CFG_BEM; +#elif defined(__BIG_ENDIAN) + dev->CFG_cache |= CFG_BEM; +#else +#error This driver only works for big or little endian!!! +#endif + + writel(dev->CFG_cache, dev->base + CFG); + dprintk("CFG: %08x\n", dev->CFG_cache); + + if (readl(dev->base + SRR)) + writel(readl(dev->base+0x20c) | 0xfe00, dev->base + 0x20c); + + /* Note! The DMA burst size interacts with packet + * transmission, such that the largest packet that + * can be transmitted is 8192 - FLTH - burst size. + * If only the transmit fifo was larger... + */ + writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA1024 + | ((1600 / 32) * 0x100), + dev->base + TXCFG); + + /* Flush the interrupt holdoff timer */ + writel(0x000, dev->base + IHR); + writel(0x100, dev->base + IHR); + + /* Set Rx to full duplex, don't accept runt, errored, long or length + * range errored packets. Set MXDMA to 7 => 512 word burst + */ + writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD + | RXCFG_ALP + | RXCFG_MXDMA | 0, dev->base + RXCFG); + + /* Disable priority queueing */ + writel(0, dev->base + PQCR); + + /* Enable IP checksum validation and detetion of VLAN headers. + * Note: do not set the reject options as at least the 0x102 + * revision of the chip does not properly accept IP fragments + * at least for UDP. + */ + writel(VRCR_IPEN | VRCR_VTDEN, dev->base + VRCR); + + /* Enable per-packet TCP/UDP/IP checksumming */ + writel(VTCR_PPCHK, dev->base + VTCR); + + /* Disable Pause frames */ + writel(0, dev->base + PCR); + + /* Disable Wake On Lan */ + writel(0, dev->base + WCSR); + + ns83820_getmac(dev, dev->net_dev.dev_addr); + + /* Yes, we support dumb IP checksum on transmit */ + dev->net_dev.features |= NETIF_F_SG; + dev->net_dev.features |= NETIF_F_IP_CSUM; +#if defined(USE_64BIT_ADDR) || defined(CONFIG_HIGHMEM4G) + dev->net_dev.features |= NETIF_F_HIGHDMA; +#endif + + register_netdev(&dev->net_dev); + + printk(KERN_INFO "%s: ns83820.c v" VERSION ": DP83820 %02x:%02x:%02x:%02x:%02x:%02x pciaddr=0x%08lx irq=%d rev 0x%x\n", + dev->net_dev.name, + dev->net_dev.dev_addr[0], dev->net_dev.dev_addr[1], + dev->net_dev.dev_addr[2], dev->net_dev.dev_addr[3], + dev->net_dev.dev_addr[4], dev->net_dev.dev_addr[5], + addr, pci_dev->irq, + (unsigned)readl(dev->base + SRR) + ); + + return 0; + +out_unmap: + iounmap(dev->base); +out_disable: + pci_free_consistent(pci_dev, 4 * DESC_SIZE * NR_TX_DESC, dev->tx_descs, dev->tx_phy_descs); + pci_free_consistent(pci_dev, 4 * DESC_SIZE * NR_RX_DESC, dev->rx_info.descs, dev->rx_info.phy_descs); + pci_disable_device(pci_dev); +out_free: + kfree(dev); +out: + return err; +} + +static struct pci_device_id pci_device_id[] __devinitdata = { + { 0x100b, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + { 0, }, +}; + +static struct pci_driver driver = { + name: "ns83820", + id_table: pci_device_id, + probe: ns83820_probe, +#if 0 /* FIXME: implement */ + remove: , + suspend: , + resume: , +#endif +}; + + +static int __init ns83820_init(void) +{ + printk(KERN_INFO "ns83820.c: National Semiconductor DP83820 10/100/100 driver.\n"); + return pci_module_init(&driver); +} + +static void ns83820_exit(void) +{ + struct ns83820 *dev; + + for (dev = ns83820_chain; dev; ) { + struct ns83820 *next = dev->next_dev; + + writel(0, dev->base + IMR); /* paranoia */ + writel(0, dev->base + IER); + readl(dev->base + IER); + + unregister_netdev(&dev->net_dev); + free_irq(dev->pci_dev->irq, dev); + iounmap(dev->base); + pci_free_consistent(dev->pci_dev, 4 * DESC_SIZE * NR_TX_DESC, + dev->tx_descs, dev->tx_phy_descs); + pci_free_consistent(dev->pci_dev, 4 * DESC_SIZE * NR_RX_DESC, + dev->rx_info.descs, dev->rx_info.phy_descs); + pci_disable_device(dev->pci_dev); + kfree(dev); + dev = next; + } + pci_unregister_driver(&driver); + ns83820_chain = NULL; +} + +MODULE_AUTHOR("Benjamin LaHaise <bcrl@redhat.com>"); +MODULE_DESCRIPTION("National Semiconductor DP83820 10/100/1000 driver"); +MODULE_DEVICE_TABLE(pci, pci_device_id); +module_init(ns83820_init); +module_exit(ns83820_exit); diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index fb21b3c663d8..37d965573a5f 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -179,6 +179,7 @@ static int full_duplex[MAX_UNITS]; * v1.25kf Added No Interrupt on successful Tx for some Tx's <kaf@fc.hp.com> * v1.26 Converted to pci_alloc_consistent, Jamey Hicks / George France * <jamey@crl.dec.com> + * v1.26p Fix oops on rmmod+insmod; plug i/o resource leak - Paul Gortmaker */ @@ -471,7 +472,7 @@ static int __init pcnet32_probe_vlbus(int cards_found) -static int __init +static int __devinit pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent) { static int card_idx; @@ -506,10 +507,11 @@ pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent) * Called from both pcnet32_probe_vlbus and pcnet_probe_pci. * pdev will be NULL when called from pcnet32_probe_vlbus. */ -static int __init +static int __devinit pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev) { struct pcnet32_private *lp; + struct resource *res; dma_addr_t lp_dma_addr; int i,media,fdx = 0, mii = 0, fset = 0; #ifdef DO_DXSUFLO @@ -643,7 +645,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car } if( memcmp( promaddr, dev->dev_addr, 6) ) { - printk(" warning PROM address does not match CSR address"); + printk(" warning PROM address does not match CSR address\n"); #if defined(__i386__) printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); memcpy(dev->dev_addr, promaddr, 6); @@ -682,11 +684,15 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car } dev->base_addr = ioaddr; - request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname); + res = request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname); + if (res == NULL) + return -EBUSY; /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */ - if ((lp = pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) + if ((lp = pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) { + release_resource(res); return -ENOMEM; + } memset(lp, 0, sizeof(*lp)); lp->dma_addr = lp_dma_addr; @@ -715,6 +721,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car if (a == NULL) { printk(KERN_ERR "pcnet32: No access methods\n"); pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + release_resource(res); return -ENODEV; } lp->a = *a; @@ -762,6 +769,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int car else { printk(", failed to detect IRQ line.\n"); pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + release_resource(res); return -ENODEV; } } @@ -1579,6 +1587,8 @@ static void __exit pcnet32_cleanup_module(void) next_dev = lp->next; unregister_netdev(pcnet32_dev); release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE); + if (lp->pci_dev != NULL) + pci_unregister_driver(&pcnet32_driver); pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); kfree(pcnet32_dev); pcnet32_dev = next_dev; diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 9f1896bcc369..fd1a5d852472 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -636,7 +636,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file, if (copy_from_user(&uprog, (void *) arg, sizeof(uprog))) break; - if (uprog.len > 0) { + if (uprog.len > 0 && uprog.len < 65536) { err = -ENOMEM; len = uprog.len * sizeof(struct sock_filter); code = kmalloc(len, GFP_KERNEL); diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index f5680e2cd2aa..a65b3b097c2b 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -600,7 +600,7 @@ static u16 sis900_default_phy(struct net_device * net_dev) /** * sis900_set_capability: - set the media capability of network adapter. * @net_dev : the net device to probe for - * @mii_phy : default PHY + * @phy : default PHY * * Set the media capability of network adapter according to * mii status register. It's necessary before auto-negotiate. @@ -1190,6 +1190,7 @@ static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_p /** * sis900_set_mode: - Set the media mode of mac register. + * @ioaddr: the address of the device * @speed : the transmit speed to be determined * @duplex: the duplex mode to be determined * diff --git a/drivers/net/sk98lin/h/skdrv1st.h b/drivers/net/sk98lin/h/skdrv1st.h index 49974a7c030e..7a7c953b7b16 100644 --- a/drivers/net/sk98lin/h/skdrv1st.h +++ b/drivers/net/sk98lin/h/skdrv1st.h @@ -113,7 +113,7 @@ typedef struct s_AC SK_AC; #include <linux/string.h> #include <linux/errno.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <asm/byteorder.h> diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 8df9e438c560..a55fa1d36e6b 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -1503,6 +1503,10 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, #ifdef CONFIG_TULIP_MWI if (!force_csr0 && (tp->flags & HAS_PCI_MWI)) tulip_mwi_config (pdev, dev); +#else + /* MWI is broken for DC21143 rev 65... */ + if (chip_idx == DC21143 && chip_rev == 65) + tp->csr0 &= ~MWI; #endif /* Stop the chip's Tx and Rx processes. */ diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 67c168b4a65c..23812dcbc2b8 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -1830,3 +1830,4 @@ fst_cleanup_module(void) module_init ( fst_init ); module_exit ( fst_cleanup_module ); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index af385d12a288..f43e33a9ab0d 100644 --- a/drivers/net/wan/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -400,7 +400,6 @@ static void sv11_shutdown(struct sv11_device *dev) static int io=0x200; static int irq=9; -#ifdef LINUX_21 MODULE_PARM(io,"i"); MODULE_PARM_DESC(io, "The I/O base of the Comtrol Hostess SV11 card"); MODULE_PARM(dma,"i"); @@ -408,9 +407,9 @@ MODULE_PARM_DESC(dma, "Set this to 1 to use DMA1/DMA3 for TX/RX"); MODULE_PARM(irq,"i"); MODULE_PARM_DESC(irq, "The interrupt line setting for the Comtrol Hostess SV11 card"); -MODULE_AUTHOR("Bulding Number Three Ltd"); +MODULE_AUTHOR("Alan Cox"); +MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Modular driver for the Comtrol Hostess SV11"); -#endif static struct sv11_device *sv11_unit; diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 3b3bd1707ac8..157ec0df84bd 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -11,7 +11,7 @@ * at http://www.granch.com (English) or http://www.granch.ru (Russian) * * This software may be used and distributed according to the terms - * of the GNU Public License. + * of the GNU General Public License. * * * 5.0.1 Jun 22 2001 @@ -1474,6 +1474,8 @@ MODULE_PARM( mac, "1-" __MODULE_STRING( SBNI_MAX_NUM_CARDS ) "i" ); MODULE_PARM( skip_pci_probe, "i" ); +MODULE_LICENSE("GPL"); + int init_module( void ) diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c index 906067d6f230..8bc2e120a1fd 100644 --- a/drivers/net/wan/sealevel.c +++ b/drivers/net/wan/sealevel.c @@ -9,6 +9,7 @@ * 2 of the License, or (at your option) any later version. * * (c) Copyright 1999 Building Number Three Ltd + * (c) Copyright 2001 Alan Cox. * */ @@ -455,7 +456,8 @@ MODULE_PARM_DESC(irq, "The interrupt line setting for the SeaLevel card"); MODULE_PARM(slow,"i"); MODULE_PARM_DESC(slow, "Set this for an older Sealevel card such as the 4012"); -MODULE_AUTHOR("Bulding Number Three Ltd"); +MODULE_AUTHOR("Alan Cox"); +MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Modular driver for the SeaLevel 4021"); #endif diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c index 940de522a7b8..abc551f57656 100644 --- a/drivers/net/wan/z85230.c +++ b/drivers/net/wan/z85230.c @@ -1739,7 +1739,7 @@ EXPORT_SYMBOL(z8530_get_stats); /* * Module support */ -static const char banner[] __initdata = KERN_INFO "Generic Z85C30/Z85230 interface driver v0.02\n"; +static char banner[] __initdata = KERN_INFO "Generic Z85C30/Z85230 interface driver v0.02\n"; static int __init z85230_init_driver(void) { @@ -1752,3 +1752,7 @@ static void __exit z85230_cleanup_driver(void) { } module_exit(z85230_cleanup_driver); + +MODULE_AUTHOR("Red Hat Inc."); +MODULE_DESCRIPTION("Z85x30 synchronous driver core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4d5a4186add3..c1f49dfd32a4 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -25,8 +25,11 @@ endif # obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o +obj-$(CONFIG_PARISC) += setup-bus.o obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o obj-$(CONFIG_ALL_PPC) += setup-bus.o +obj-$(CONFIG_DDB5476) += setup-bus.o +obj-$(CONFIG_SGI_IP27) += setup-irq.o ifndef CONFIG_X86 obj-y += syscall.o diff --git a/drivers/scsi/53c700-mem.c b/drivers/scsi/53c700-mem.c new file mode 100644 index 000000000000..4a8dd939e799 --- /dev/null +++ b/drivers/scsi/53c700-mem.c @@ -0,0 +1,1842 @@ +/* WARNING: GENERATED FILE (from 53c700.c), DO NOT MODIFY */ +#define MEM_MAPPED +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR (or Symbios) 53c700 and 53c700-66 Driver + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com +**----------------------------------------------------------------------------- +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +**----------------------------------------------------------------------------- + */ + +/* Notes: + * + * This driver is designed exclusively for these chips (virtually the + * earliest of the scripts engine chips). They need their own drivers + * because they are missing so many of the scripts and snazzy register + * features of their elder brothers (the 710, 720 and 770). + * + * The 700 is the lowliest of the line, it can only do async SCSI. + * The 700-66 can at least do synchronous SCSI up to 10MHz. + * + * The 700 chip has no host bus interface logic of its own. However, + * it is usually mapped to a location with well defined register + * offsets. Therefore, if you can determine the base address and the + * irq your board incorporating this chip uses, you can probably use + * this driver to run it (although you'll probably have to write a + * minimal wrapper for the purpose---see the NCR_D700 driver for + * details about how to do this). + * + * + * TODO List: + * + * 1. Better statistics in the proc fs + * + * 2. Implement message queue (queues SCSI messages like commands) and make + * the abort and device reset functions use them. + * */ + +/* CHANGELOG + * + * Version 2.3 + * + * More endianness/cache coherency changes. + * + * Better bad device handling (handles devices lying about tag + * queueing support and devices which fail to provide sense data on + * contingent allegiance conditions) + * + * Many thanks to Richard Hirst <rhirst@linuxcare.com> for patiently + * debugging this driver on the parisc architecture and suggesting + * many improvements and bug fixes. + * + * Thanks also go to Linuxcare Inc. for providing several PARISC + * machines for me to debug the driver on. + * + * Version 2.2 + * + * Made the driver mem or io mapped; added endian invariance; added + * dma cache flushing operations for architectures which need it; + * added support for more varied clocking speeds. + * + * Version 2.1 + * + * Initial modularisation from the D700. See NCR_D700.c for the rest of + * the changelog. + * */ +#define NCR_700_VERSION "2.3" + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/mca.h> +#include <asm/dma.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/byteorder.h> +#include <linux/blk.h> +#include <linux/module.h> + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#include "53c700.h" + +#ifdef NCR_700_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("53c700 and 53c700-66 Driver"); +MODULE_LICENSE("GPL"); + +/* This is the script */ +#include "53c700_d.h" + + +STATIC int NCR_700_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +STATIC int NCR_700_abort(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_bus_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_dev_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_host_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_proc_directory_info(char *, char **, off_t, int, int, int); +STATIC void NCR_700_chip_setup(struct Scsi_Host *host); +STATIC void NCR_700_chip_reset(struct Scsi_Host *host); + +static char *NCR_700_phase[] = { + "", + "after selection", + "before command phase", + "after command phase", + "after status phase", + "after data in phase", + "after data out phase", + "during data phase", +}; + +static char *NCR_700_condition[] = { + "", + "NOT MSG_OUT", + "UNEXPECTED PHASE", + "NOT MSG_IN", + "UNEXPECTED MSG", + "MSG_IN", + "SDTR_MSG RECEIVED", + "REJECT_MSG RECEIVED", + "DISCONNECT_MSG RECEIVED", + "MSG_OUT", + "DATA_IN", + +}; + +static char *NCR_700_fatal_messages[] = { + "unexpected message after reselection", + "still MSG_OUT after message injection", + "not MSG_IN after selection", + "Illegal message length received", +}; + +static char *NCR_700_SBCL_bits[] = { + "IO ", + "CD ", + "MSG ", + "ATN ", + "SEL ", + "BSY ", + "ACK ", + "REQ ", +}; + +static char *NCR_700_SBCL_to_phase[] = { + "DATA_OUT", + "DATA_IN", + "CMD_OUT", + "STATE", + "ILLEGAL PHASE", + "ILLEGAL PHASE", + "MSG OUT", + "MSG IN", +}; + +static __u8 NCR_700_SDTR_msg[] = { + 0x01, /* Extended message */ + 0x03, /* Extended message Length */ + 0x01, /* SDTR Extended message */ + NCR_700_MIN_PERIOD, + NCR_700_MAX_OFFSET +}; + +struct Scsi_Host * __init +NCR_700_detect(Scsi_Host_Template *tpnt, + struct NCR_700_Host_Parameters *hostdata) +{ + __u32 *script = kmalloc(sizeof(SCRIPT), GFP_KERNEL); + __u32 pScript; + struct Scsi_Host *host; + static int banner = 0; + int j; + + /* Fill in the missing routines from the host template */ + tpnt->queuecommand = NCR_700_queuecommand; + tpnt->eh_abort_handler = NCR_700_abort; + tpnt->eh_device_reset_handler = NCR_700_dev_reset; + tpnt->eh_bus_reset_handler = NCR_700_bus_reset; + tpnt->eh_host_reset_handler = NCR_700_host_reset; + tpnt->can_queue = NCR_700_COMMAND_SLOTS_PER_HOST; + tpnt->sg_tablesize = NCR_700_SG_SEGMENTS; + tpnt->cmd_per_lun = NCR_700_MAX_TAGS; + tpnt->use_clustering = DISABLE_CLUSTERING; + tpnt->use_new_eh_code = 1; + tpnt->proc_info = NCR_700_proc_directory_info; + + if(tpnt->name == NULL) + tpnt->name = "53c700"; + if(tpnt->proc_name == NULL) + tpnt->proc_name = "53c700"; + + + if((host = scsi_register(tpnt, 4)) == NULL) + return NULL; + if(script == NULL) { + printk(KERN_ERR "53c700: Failed to allocate script, detatching\n"); + scsi_unregister(host); + return NULL; + } + + hostdata->slots = kmalloc(sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST, GFP_KERNEL); + if(hostdata->slots == NULL) { + printk(KERN_ERR "53c700: Failed to allocate command slots, detatching\n"); + scsi_unregister(host); + return NULL; + } + memset(hostdata->slots, 0, sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST); + for(j = 0; j < NCR_700_COMMAND_SLOTS_PER_HOST; j++) { + if(j == 0) + hostdata->free_list = &hostdata->slots[j]; + else + hostdata->slots[j-1].ITL_forw = &hostdata->slots[j]; + hostdata->slots[j].state = NCR_700_SLOT_FREE; + } + host->hostdata[0] = (__u32)hostdata; + for(j = 0; j < sizeof(SCRIPT)/sizeof(SCRIPT[0]); j++) { + script[j] = bS_to_host(SCRIPT[j]); + } + /* bus physical address of script */ + pScript = virt_to_bus(script); + /* adjust all labels to be bus physical */ + for(j = 0; j < PATCHES; j++) { + script[LABELPATCHES[j]] = bS_to_host(pScript + SCRIPT[LABELPATCHES[j]]); + } + /* now patch up fixed addresses */ + script_patch_32(script, MessageLocation, + virt_to_bus(&hostdata->msgout[0])); + script_patch_32(script, StatusAddress, + virt_to_bus(&hostdata->status)); + script_patch_32(script, ReceiveMsgAddress, + virt_to_bus(&hostdata->msgin[0])); + + hostdata->script = script; + hostdata->pScript = pScript; + hostdata->state = NCR_700_HOST_FREE; + spin_lock_init(&hostdata->lock); + hostdata->cmd = NULL; + host->max_id = 7; + host->max_lun = NCR_700_MAX_LUNS; + host->unique_id = hostdata->base; + host->base = hostdata->base; + host->hostdata[0] = (unsigned long)hostdata; + /* kick the chip */ + NCR_700_writeb(0xff, host, CTEST9_REG); + hostdata->rev = (NCR_700_readb(host, CTEST7_REG)<<4) & 0x0f; + hostdata->fast = (NCR_700_readb(host, CTEST9_REG) == 0); + if(banner == 0) { + printk(KERN_NOTICE "53c700: Version " NCR_700_VERSION " By James.Bottomley@HansenPartnership.com\n"); + banner = 1; + } + printk(KERN_NOTICE "scsi%d: %s rev %d %s\n", host->host_no, + hostdata->fast ? "53c700-66" : "53c700", + hostdata->rev, hostdata->differential ? + "(Differential)" : ""); + /* reset the chip */ + NCR_700_chip_reset(host); + NCR_700_writeb(ASYNC_OPERATION , host, SXFER_REG); + + return host; +} + +int +NCR_700_release(struct Scsi_Host *host) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + kfree(hostdata->script); + return 1; +} + +static inline __u8 +NCR_700_identify(int can_disconnect, __u8 lun) +{ + return IDENTIFY_BASE | + ((can_disconnect) ? 0x40 : 0) | + (lun & NCR_700_LUN_MASK); +} + +/* + * Function : static int datapath_residual (Scsi_Host *host) + * + * Purpose : return residual data count of what's in the chip. If you + * really want to know what this function is doing, it's almost a + * direct transcription of the algorithm described in the 53c710 + * guide, except that the DBC and DFIFO registers are only 6 bits + * wide. + * + * Inputs : host - SCSI host */ +static inline int +NCR_700_data_residual (struct Scsi_Host *host) { + int count, synchronous; + unsigned int ddir; + + count = ((NCR_700_readb(host, DFIFO_REG) & 0x3f) - + (NCR_700_readl(host, DBC_REG) & 0x3f)) & 0x3f; + + synchronous = NCR_700_readb(host, SXFER_REG) & 0x0f; + + /* get the data direction */ + ddir = NCR_700_readb(host, CTEST0_REG) & 0x01; + + if (ddir) { + /* Receive */ + if (synchronous) + count += (NCR_700_readb(host, SSTAT2_REG) & 0xf0) >> 4; + else + if (NCR_700_readb(host, SSTAT1_REG) & SIDL_REG_FULL) + ++count; + } else { + /* Send */ + __u8 sstat = NCR_700_readb(host, SSTAT1_REG); + if (sstat & SODL_REG_FULL) + ++count; + if (synchronous && (sstat & SODR_REG_FULL)) + ++count; + } + return count; +} + +/* print out the SCSI wires and corresponding phase from the SBCL register + * in the chip */ +static inline char * +sbcl_to_string(__u8 sbcl) +{ + int i; + static char ret[256]; + + ret[0]='\0'; + for(i=0; i<8; i++) { + if((1<<i) & sbcl) + strcat(ret, NCR_700_SBCL_bits[i]); + } + strcat(ret, NCR_700_SBCL_to_phase[sbcl & 0x07]); + return ret; +} + +static inline __u8 +bitmap_to_number(__u8 bitmap) +{ + __u8 i; + + for(i=0; i<8 && !(bitmap &(1<<i)); i++) + ; + return i; +} + +/* Pull a slot off the free list */ +STATIC struct NCR_700_command_slot * +find_empty_slot(struct NCR_700_Host_Parameters *hostdata) +{ + struct NCR_700_command_slot *slot = hostdata->free_list; + + if(slot == NULL) { + /* sanity check */ + if(hostdata->command_slot_count != NCR_700_COMMAND_SLOTS_PER_HOST) + printk(KERN_ERR "SLOTS FULL, but count is %d, should be %d\n", hostdata->command_slot_count, NCR_700_COMMAND_SLOTS_PER_HOST); + return NULL; + } + + if(slot->state != NCR_700_SLOT_FREE) + /* should panic! */ + printk(KERN_ERR "BUSY SLOT ON FREE LIST!!!\n"); + + + hostdata->free_list = slot->ITL_forw; + slot->ITL_forw = NULL; + + + /* NOTE: set the state to busy here, not queued, since this + * indicates the slot is in use and cannot be run by the IRQ + * finish routine. If we cannot queue the command when it + * is properly build, we then change to NCR_700_SLOT_QUEUED */ + slot->state = NCR_700_SLOT_BUSY; + hostdata->command_slot_count++; + + return slot; +} + +STATIC void +free_slot(struct NCR_700_command_slot *slot, + struct NCR_700_Host_Parameters *hostdata) +{ + int hash; + struct NCR_700_command_slot **forw, **back; + + + if((slot->state & NCR_700_SLOT_MASK) != NCR_700_SLOT_MAGIC) { + printk(KERN_ERR "53c700: SLOT %p is not MAGIC!!!\n", slot); + } + if(slot->state == NCR_700_SLOT_FREE) { + printk(KERN_ERR "53c700: SLOT %p is FREE!!!\n", slot); + } + /* remove from queues */ + if(slot->tag != NCR_700_NO_TAG) { + hash = hash_ITLQ(slot->cmnd->target, slot->cmnd->lun, + slot->tag); + if(slot->ITLQ_forw == NULL) + back = &hostdata->ITLQ_Hash_back[hash]; + else + back = &slot->ITLQ_forw->ITLQ_back; + + if(slot->ITLQ_back == NULL) + forw = &hostdata->ITLQ_Hash_forw[hash]; + else + forw = &slot->ITLQ_back->ITLQ_forw; + + *forw = slot->ITLQ_forw; + *back = slot->ITLQ_back; + } + hash = hash_ITL(slot->cmnd->target, slot->cmnd->lun); + if(slot->ITL_forw == NULL) + back = &hostdata->ITL_Hash_back[hash]; + else + back = &slot->ITL_forw->ITL_back; + + if(slot->ITL_back == NULL) + forw = &hostdata->ITL_Hash_forw[hash]; + else + forw = &slot->ITL_back->ITL_forw; + + *forw = slot->ITL_forw; + *back = slot->ITL_back; + + slot->resume_offset = 0; + slot->cmnd = NULL; + slot->state = NCR_700_SLOT_FREE; + slot->ITL_forw = hostdata->free_list; + hostdata->free_list = slot; + hostdata->command_slot_count--; +} + + +/* This routine really does very little. The command is indexed on + the ITL and (if tagged) the ITLQ lists in _queuecommand */ +STATIC void +save_for_reselection(struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp) +{ + /* Its just possible that this gets executed twice */ + if(SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + slot->resume_offset = dsp; + } + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; +} + +/* Most likely nexus is the oldest in each case */ +STATIC inline struct NCR_700_command_slot * +find_ITL_Nexus(struct NCR_700_Host_Parameters *hostdata, __u8 pun, __u8 lun) +{ + int hash = hash_ITL(pun, lun); + struct NCR_700_command_slot *slot = hostdata->ITL_Hash_back[hash]; + while(slot != NULL && !(slot->cmnd->target == pun && + slot->cmnd->lun == lun)) + slot = slot->ITL_back; + return slot; +} + +STATIC inline struct NCR_700_command_slot * +find_ITLQ_Nexus(struct NCR_700_Host_Parameters *hostdata, __u8 pun, + __u8 lun, __u8 tag) +{ + int hash = hash_ITLQ(pun, lun, tag); + struct NCR_700_command_slot *slot = hostdata->ITLQ_Hash_back[hash]; + + while(slot != NULL && !(slot->cmnd->target == pun + && slot->cmnd->lun == lun && slot->tag == tag)) + slot = slot->ITLQ_back; + +#ifdef NCR_700_TAG_DEBUG + if(slot != NULL) { + struct NCR_700_command_slot *n = slot->ITLQ_back; + while(n != NULL && n->cmnd->target != pun + && n->cmnd->lun != lun && n->tag != tag) + n = n->ITLQ_back; + + if(n != NULL && n->cmnd->target == pun && n->cmnd->lun == lun + && n->tag == tag) { + printk(KERN_WARNING "53c700: WARNING: DUPLICATE tag %d\n", + tag); + } + } +#endif + return slot; +} + + + +/* This translates the SDTR message offset and period to a value + * which can be loaded into the SXFER_REG. + * + * NOTE: According to SCSI-2, the true transfer period (in ns) is + * actually four times this period value */ +STATIC inline __u8 +NCR_700_offset_period_to_sxfer(struct NCR_700_Host_Parameters *hostdata, + __u8 offset, __u8 period) +{ + int XFERP; + + if(period*4 < NCR_700_MIN_PERIOD) { + printk(KERN_WARNING "53c700: Period %dns is less than SCSI-2 minimum, setting to %d\n", period*4, NCR_700_MIN_PERIOD); + period = NCR_700_MIN_PERIOD/4; + } + XFERP = (period*4 * hostdata->sync_clock)/1000 - 4; + if(offset > NCR_700_MAX_OFFSET) { + printk(KERN_WARNING "53c700: Offset %d exceeds maximum, setting to %d\n", + offset, NCR_700_MAX_OFFSET); + offset = NCR_700_MAX_OFFSET; + } + if(XFERP < NCR_700_MIN_XFERP) { + printk(KERN_WARNING "53c700: XFERP %d is less than minium, setting to %d\n", + XFERP, NCR_700_MIN_XFERP); + XFERP = NCR_700_MIN_XFERP; + } + return (offset & 0x0f) | (XFERP & 0x07)<<4; +} + + +STATIC inline void +NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, int result) +{ + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + + if(SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + if(SCp->cmnd[0] == REQUEST_SENSE && SCp->cmnd[6] == NCR_700_INTERNAL_SENSE_MAGIC) { +#ifdef NCR_700_DEBUG + printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is", + SCp, SCp->cmnd[7], result); + print_sense("53c700", SCp); +#endif + if(result == 0) + result = SCp->cmnd[7]; + } + + free_slot(slot, hostdata); + + SCp->host_scribble = NULL; + SCp->result = result; + SCp->scsi_done(SCp); + if(NCR_700_get_depth(SCp->device) == 0 || + NCR_700_get_depth(SCp->device) > NCR_700_MAX_TAGS) + printk(KERN_ERR "Invalid depth in NCR_700_scsi_done(): %d\n", + NCR_700_get_depth(SCp->device)); + NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) - 1); + } else { + printk(KERN_ERR "53c700: SCSI DONE HAS NULL SCp\n"); + } +} + + +STATIC void +NCR_700_internal_bus_reset(struct Scsi_Host *host) +{ + /* Bus reset */ + NCR_700_writeb(ASSERT_RST, host, SCNTL1_REG); + udelay(50); + NCR_700_writeb(0, host, SCNTL1_REG); + +} + +STATIC void +NCR_700_chip_setup(struct Scsi_Host *host) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + NCR_700_writeb(1 << host->this_id, host, SCID_REG); + NCR_700_writeb(0, host, SBCL_REG); + NCR_700_writeb(0, host, SXFER_REG); + + NCR_700_writeb(PHASE_MM_INT | SEL_TIMEOUT_INT | GROSS_ERR_INT | UX_DISC_INT + | RST_INT | PAR_ERR_INT | SELECT_INT, host, SIEN_REG); + + NCR_700_writeb(ABORT_INT | INT_INST_INT | ILGL_INST_INT, host, DIEN_REG); + NCR_700_writeb(BURST_LENGTH_8, host, DMODE_REG); + NCR_700_writeb(FULL_ARBITRATION | PARITY | AUTO_ATN, host, SCNTL0_REG); + NCR_700_writeb(LAST_DIS_ENBL | ENABLE_ACTIVE_NEGATION|GENERATE_RECEIVE_PARITY, + host, CTEST8_REG); + NCR_700_writeb(ENABLE_SELECT, host, SCNTL1_REG); + if(hostdata->clock > 75) { + printk(KERN_ERR "53c700: Clock speed %dMHz is too high: 75Mhz is the maximum this chip can be driven at\n", hostdata->clock); + /* do the best we can, but the async clock will be out + * of spec: sync divider 2, async divider 3 */ + DEBUG(("53c700: sync 2 async 3\n")); + NCR_700_writeb(SYNC_DIV_2_0, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_3_0, host, DCNTL_REG); + hostdata->sync_clock = hostdata->clock/2; + } else if(hostdata->clock > 50 && hostdata->clock <= 75) { + /* sync divider 1.5, async divider 3 */ + DEBUG(("53c700: sync 1.5 async 3\n")); + NCR_700_writeb(SYNC_DIV_1_5, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_3_0, host, DCNTL_REG); + hostdata->sync_clock = hostdata->clock*2; + hostdata->sync_clock /= 3; + + } else if(hostdata->clock > 37 && hostdata->clock <= 50) { + /* sync divider 1, async divider 2 */ + DEBUG(("53c700: sync 1 async 2\n")); + NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_2_0, host, DCNTL_REG); + hostdata->sync_clock = hostdata->clock; + } else if(hostdata->clock > 25 && hostdata->clock <=37) { + /* sync divider 1, async divider 1.5 */ + DEBUG(("53c700: sync 1 async 1.5\n")); + NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_1_5, host, DCNTL_REG); + hostdata->sync_clock = hostdata->clock; + } else { + DEBUG(("53c700: sync 1 async 1\n")); + NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_1_0, host, DCNTL_REG); + /* sync divider 1, async divider 1 */ + } +} + +STATIC void +NCR_700_chip_reset(struct Scsi_Host *host) +{ + /* Chip reset */ + NCR_700_writeb(SOFTWARE_RESET, host, DCNTL_REG); + udelay(100); + + NCR_700_writeb(0, host, DCNTL_REG); + + mdelay(1000); + + NCR_700_chip_setup(host); +} + +/* The heart of the message processing engine is that the instruction + * immediately after the INT is the normal case (and so must be CLEAR + * ACK). If we want to do something else, we call that routine in + * scripts and set temp to be the normal case + 8 (skipping the CLEAR + * ACK) so that the routine returns correctly to resume its activity + * */ +STATIC __u32 +process_extended_message(struct Scsi_Host *host, + struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp, __u32 dsps) +{ + __u32 resume_offset = dsp, temp = dsp + 8; + __u8 pun = 0xff, lun = 0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + switch(hostdata->msgin[2]) { + case A_SDTR_MSG: + if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { + __u8 period = hostdata->msgin[3]; + __u8 offset = hostdata->msgin[4]; + __u8 sxfer; + + if(offset != 0 && period != 0) + sxfer = NCR_700_offset_period_to_sxfer(hostdata, offset, period); + else + sxfer = 0; + + if(sxfer != NCR_700_get_SXFER(SCp->device)) { + printk(KERN_INFO "scsi%d: (%d:%d) Synchronous at offset %d, period %dns\n", + host->host_no, pun, lun, + offset, period*4); + + NCR_700_set_SXFER(SCp->device, sxfer); + } + + + NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + + NCR_700_writeb(NCR_700_get_SXFER(SCp->device), + host, SXFER_REG); + + } else { + /* SDTR message out of the blue, reject it */ + printk(KERN_WARNING "scsi%d Unexpected SDTR msg\n", + host->host_no); + hostdata->msgout[0] = A_REJECT_MSG; + dma_cache_wback((unsigned long)hostdata->msgout, sizeof(hostdata->msgout)); + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + } + break; + + case A_WDTR_MSG: + printk(KERN_INFO "scsi%d: (%d:%d), Unsolicited WDTR after CMD, Rejecting\n", + host->host_no, pun, lun); + hostdata->msgout[0] = A_REJECT_MSG; + dma_cache_wback((unsigned long)hostdata->msgout, sizeof(hostdata->msgout)); + script_patch_16(hostdata->script, MessageCount, 1); + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + + break; + + default: + printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + print_msg(hostdata->msgin); + printk("\n"); + /* just reject it */ + hostdata->msgout[0] = A_REJECT_MSG; + dma_cache_wback((unsigned long)hostdata->msgout, sizeof(hostdata->msgout)); + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + } + NCR_700_writel(temp, host, TEMP_REG); + return resume_offset; +} + +STATIC __u32 +process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp, __u32 dsps) +{ + /* work out where to return to */ + __u32 temp = dsp + 8, resume_offset = dsp; + __u8 pun = 0xff, lun = 0xff; + + dma_cache_inv((unsigned long)hostdata->msgin, sizeof(hostdata->msgin)); + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + +#ifdef NCR_700_DEBUG + printk("scsi%d (%d:%d): message %s: ", host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + print_msg(hostdata->msgin); + printk("\n"); +#endif + + switch(hostdata->msgin[0]) { + + case A_EXTENDED_MSG: + return process_extended_message(host, hostdata, SCp, + dsp, dsps); + + case A_REJECT_MSG: + if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { + /* Rejected our sync negotiation attempt */ + NCR_700_set_SXFER(SCp->device, 0); + NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + } else if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING)) { + /* rejected our first simple tag message */ + printk(KERN_WARNING "scsi%d (%d:%d) Rejected first tag queue attempt, turning off tag queueing\n", host->host_no, pun, lun); + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + hostdata->tag_negotiated &= ~(1<<SCp->target); + } else { + printk(KERN_WARNING "scsi%d (%d:%d) Unexpected REJECT Message %s\n", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + /* however, just ignore it */ + } + break; + + case A_PARITY_ERROR_MSG: + printk(KERN_ERR "scsi%d (%d:%d) Parity Error!\n", host->host_no, + pun, lun); + NCR_700_internal_bus_reset(host); + break; + case A_SIMPLE_TAG_MSG: + printk(KERN_INFO "scsi%d (%d:%d) SIMPLE TAG %d %s\n", host->host_no, + pun, lun, hostdata->msgin[1], + NCR_700_phase[(dsps & 0xf00) >> 8]); + /* just ignore it */ + break; + default: + printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + + print_msg(hostdata->msgin); + printk("\n"); + /* just reject it */ + hostdata->msgout[0] = A_REJECT_MSG; + dma_cache_wback((unsigned long)hostdata->msgout, sizeof(hostdata->msgout)); + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + + break; + } + NCR_700_writel(temp, host, TEMP_REG); + return resume_offset; +} + +STATIC __u32 +process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp, + struct Scsi_Host *host, + struct NCR_700_Host_Parameters *hostdata) +{ + __u32 resume_offset = 0; + __u8 pun = 0xff, lun=0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + if(dsps == A_GOOD_STATUS_AFTER_STATUS) { + dma_cache_inv((unsigned long)hostdata->status, sizeof(hostdata->status)); + DEBUG((" COMMAND COMPLETE, status=%02x\n", + hostdata->status)); + /* OK, if TCQ still on, we know it works */ + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + /* check for contingent allegiance contitions */ + if(status_byte(hostdata->status) == CHECK_CONDITION || + status_byte(hostdata->status) == COMMAND_TERMINATED) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + if(SCp->cmnd[0] == REQUEST_SENSE) { + /* OOPS: bad device, returning another + * contingent allegiance condition */ + printk(KERN_ERR "scsi%d (%d:%d) broken device is looping in contingent allegiance: ignoring\n", host->host_no, pun, lun); + NCR_700_scsi_done(hostdata, SCp, hostdata->status); + } else { + + DEBUG((" cmd %p has status %d, requesting sense\n", + SCp, hostdata->status)); + /* we can destroy the command here because the + * contingent allegiance condition will cause a + * retry which will re-copy the command from the + * saved data_cmnd */ + SCp->cmnd[0] = REQUEST_SENSE; + SCp->cmnd[1] = (SCp->lun & 0x7) << 5; + SCp->cmnd[2] = 0; + SCp->cmnd[3] = 0; + SCp->cmnd[4] = sizeof(SCp->sense_buffer); + SCp->cmnd[5] = 0; + SCp->cmd_len = 6; + /* Here's a quiet hack: the REQUEST_SENSE command is + * six bytes, so store a flag indicating that this + * was an internal sense request and the original + * status at the end of the command */ + SCp->cmnd[6] = NCR_700_INTERNAL_SENSE_MAGIC; + SCp->cmnd[7] = hostdata->status; + slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer)); + slot->SG[0].pAddr = bS_to_host(virt_to_bus(SCp->sense_buffer)); + slot->SG[1].ins = bS_to_host(SCRIPT_RETURN); + slot->SG[1].pAddr = 0; + slot->resume_offset = hostdata->pScript; + dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG[0])*2); + dma_cache_inv((unsigned long)SCp->sense_buffer, sizeof(SCp->sense_buffer)); + + /* queue the command for reissue */ + slot->state = NCR_700_SLOT_QUEUED; + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + } + } else { + if(status_byte(hostdata->status) == GOOD && + SCp->cmnd[0] == INQUIRY && SCp->use_sg == 0) { + /* Piggy back the tag queueing support + * on this command */ + if(((char *)SCp->request_buffer)[7] & 0x02) { + printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", host->host_no, pun, lun); + hostdata->tag_negotiated |= (1<<SCp->target); + NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + } else { + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + hostdata->tag_negotiated &= ~(1<<SCp->target); + } + } + NCR_700_scsi_done(hostdata, SCp, hostdata->status); + } + } else if((dsps & 0xfffff0f0) == A_UNEXPECTED_PHASE) { + __u8 i = (dsps & 0xf00) >> 8; + + printk(KERN_ERR "scsi%d: (%d:%d), UNEXPECTED PHASE %s (%s)\n", + host->host_no, pun, lun, + NCR_700_phase[i], + sbcl_to_string(NCR_700_readb(host, SBCL_REG))); + printk(KERN_ERR " len = %d, cmd =", SCp->cmd_len); + print_command(SCp->cmnd); + + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff000) == A_FATAL) { + int i = (dsps & 0xfff); + + printk(KERN_ERR "scsi%d: (%d:%d) FATAL ERROR: %s\n", + host->host_no, pun, lun, NCR_700_fatal_messages[i]); + if(dsps == A_FATAL_ILLEGAL_MSG_LENGTH) { + printk(KERN_ERR " msg begins %02x %02x\n", + hostdata->msgin[0], hostdata->msgin[1]); + } + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff0f0) == A_DISCONNECT) { +#ifdef NCR_700_DEBUG + __u8 i = (dsps & 0xf00) >> 8; + + printk("scsi%d: (%d:%d), DISCONNECTED (%d) %s\n", + host->host_no, pun, lun, + i, NCR_700_phase[i]); +#endif + save_for_reselection(hostdata, SCp, dsp); + + } else if(dsps == A_RESELECTION_IDENTIFIED) { + __u8 lun; + struct NCR_700_command_slot *slot; + __u8 reselection_id = hostdata->reselection_id; + + dma_cache_inv((unsigned long)hostdata->msgin, sizeof(hostdata->msgin)); + + lun = hostdata->msgin[0] & 0x1f; + + hostdata->reselection_id = 0xff; + DEBUG(("scsi%d: (%d:%d) RESELECTED!\n", + host->host_no, reselection_id, lun)); + /* clear the reselection indicator */ + if(hostdata->msgin[1] == A_SIMPLE_TAG_MSG) { + slot = find_ITLQ_Nexus(hostdata, reselection_id, + lun, hostdata->msgin[2]); + } else { + slot = find_ITL_Nexus(hostdata, reselection_id, lun); + } + retry: + if(slot == NULL) { + struct NCR_700_command_slot *s = find_ITL_Nexus(hostdata, reselection_id, lun); + printk(KERN_ERR "scsi%d: (%d:%d) RESELECTED but no saved command (MSG = %02x %02x %02x)!!\n", + host->host_no, reselection_id, lun, + hostdata->msgin[0], hostdata->msgin[1], + hostdata->msgin[2]); + printk(KERN_ERR " OUTSTANDING TAGS:"); + while(s != NULL) { + if(s->cmnd->target == reselection_id && + s->cmnd->lun == lun) { + printk("%d ", s->tag); + if(s->tag == hostdata->msgin[2]) { + printk(" ***FOUND*** \n"); + slot = s; + goto retry; + } + + } + s = s->ITL_back; + } + printk("\n"); + } else { + if(hostdata->state != NCR_700_HOST_BUSY) + printk(KERN_ERR "scsi%d: FATAL, host not busy during valid reselection!\n", + host->host_no); + resume_offset = slot->resume_offset; + hostdata->cmd = slot->cmnd; + + /* re-patch for this command */ + script_patch_32_abs(hostdata->script, CommandAddress, + virt_to_bus(slot->cmnd->cmnd)); + script_patch_16(hostdata->script, + CommandCount, slot->cmnd->cmd_len); + script_patch_32_abs(hostdata->script, SGScriptStartAddress, + virt_to_bus(&slot->SG[0].ins)); + + /* Note: setting SXFER only works if we're + * still in the MESSAGE phase, so it is vital + * that ACK is still asserted when we process + * the reselection message. The resume offset + * should therefore always clear ACK */ + NCR_700_writeb(NCR_700_get_SXFER(hostdata->cmd->device), + host, SXFER_REG); + + } + } else if(dsps == A_RESELECTED_DURING_SELECTION) { + + /* This section is full of debugging code because I've + * never managed to reach it. I think what happens is + * that, because the 700 runs with selection + * interrupts enabled the whole time that we take a + * selection interrupt before we manage to get to the + * reselected script interrupt */ + + __u8 reselection_id = NCR_700_readb(host, SFBR_REG); + struct NCR_700_command_slot *slot; + + /* Take out our own ID */ + reselection_id &= ~(1<<host->this_id); + + printk(KERN_INFO "scsi%d: (%d:%d) RESELECTION DURING SELECTION, dsp=%p[%04x] state=%d, count=%d\n", + host->host_no, reselection_id, lun, (void *)dsp, dsp - hostdata->pScript, hostdata->state, hostdata->command_slot_count); + + { + /* FIXME: DEBUGGING CODE */ + __u32 SG = (__u32)bus_to_virt(hostdata->script[A_SGScriptStartAddress_used[0]]); + int i; + + for(i=0; i< NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + if(SG >= (__u32)(&hostdata->slots[i].SG[0]) + && SG <= (__u32)(&hostdata->slots[i].SG[NCR_700_SG_SEGMENTS])) + break; + } + printk(KERN_INFO "IDENTIFIED SG segment as being %p in slot %p, cmd %p, slot->resume_offset=%p\n", (void *)SG, &hostdata->slots[i], hostdata->slots[i].cmnd, (void *)hostdata->slots[i].resume_offset); + SCp = hostdata->slots[i].cmnd; + } + + if(SCp != NULL) { + slot = (struct NCR_700_command_slot *)SCp->host_scribble; + /* change slot from busy to queued to redo command */ + slot->state = NCR_700_SLOT_QUEUED; + } + hostdata->cmd = NULL; + + if(reselection_id == 0) { + if(hostdata->reselection_id == 0xff) { + printk(KERN_ERR "scsi%d: Invalid reselection during selection!!\n", host->host_no); + return 0; + } else { + printk(KERN_ERR "scsi%d: script reselected and we took a selection interrupt\n", + host->host_no); + reselection_id = hostdata->reselection_id; + } + } else { + + /* convert to real ID */ + reselection_id = bitmap_to_number(reselection_id); + } + hostdata->reselection_id = reselection_id; + hostdata->msgin[1] = 0; + dma_cache_wback((unsigned long)hostdata->msgin, sizeof(hostdata->msgin)); + if(hostdata->tag_negotiated & (1<<reselection_id)) { + resume_offset = hostdata->pScript + Ent_GetReselectionWithTag; + } else { + resume_offset = hostdata->pScript + Ent_GetReselectionData; + } + } else if(dsps == A_COMPLETED_SELECTION_AS_TARGET) { + /* we've just disconnected from the bus, do nothing since + * a return here will re-run the queued command slot + * that may have been interrupted by the initial selection */ + DEBUG((" SELECTION COMPLETED\n")); + } else if((dsps & 0xfffff0f0) == A_MSG_IN) { + resume_offset = process_message(host, hostdata, SCp, + dsp, dsps); + } else if((dsps & 0xfffff000) == 0) { + __u8 i = (dsps & 0xf0) >> 4, j = (dsps & 0xf00) >> 8; + printk(KERN_ERR "scsi%d: (%d:%d), unhandled script condition %s %s at %04x\n", + host->host_no, pun, lun, NCR_700_condition[i], + NCR_700_phase[j], dsp - hostdata->pScript); + if(SCp != NULL) { + print_command(SCp->cmnd); + + if(SCp->use_sg) { + for(i = 0; i < SCp->use_sg + 1; i++) { + printk(KERN_INFO " SG[%d].length = %d, move_insn=%08x, addr %08x\n", i, ((struct scatterlist *)SCp->buffer)[i].length, ((struct NCR_700_command_slot *)SCp->host_scribble)->SG[i].ins, ((struct NCR_700_command_slot *)SCp->host_scribble)->SG[i].pAddr); + } + } + } + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff000) == A_DEBUG_INTERRUPT) { + printk(KERN_NOTICE "scsi%d (%d:%d) DEBUG INTERRUPT %d AT %p[%04x], continuing\n", + host->host_no, pun, lun, dsps & 0xfff, (void *)dsp, dsp - hostdata->pScript); + resume_offset = dsp; + } else { + printk(KERN_ERR "scsi%d: (%d:%d), unidentified script interrupt 0x%x at %04x\n", + host->host_no, pun, lun, dsps, dsp - hostdata->pScript); + NCR_700_internal_bus_reset(host); + } + return resume_offset; +} + +/* We run the 53c700 with selection interrupts always enabled. This + * means that the chip may be selected as soon as the bus frees. On a + * busy bus, this can be before the scripts engine finishes its + * processing. Therefore, part of the selection processing has to be + * to find out what the scripts engine is doing and complete the + * function if necessary (i.e. process the pending disconnect or save + * the interrupted initial selection */ +STATIC inline __u32 +process_selection(struct Scsi_Host *host, __u32 dsp) +{ + __u8 id = 0; /* Squash compiler warning */ + int count = 0; + __u32 resume_offset = 0; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + Scsi_Cmnd *SCp = hostdata->cmd; + __u8 sbcl; + + for(count = 0; count < 5; count++) { + id = NCR_700_readb(host, SFBR_REG); + + /* Take out our own ID */ + id &= ~(1<<host->this_id); + if(id != 0) + break; + udelay(5); + } + sbcl = NCR_700_readb(host, SBCL_REG); + if((sbcl & SBCL_IO) == 0) { + /* mark as having been selected rather than reselected */ + id = 0xff; + } else { + /* convert to real ID */ + hostdata->reselection_id = id = bitmap_to_number(id); + DEBUG(("scsi%d: Reselected by %d\n", + host->host_no, id)); + } + if(hostdata->state == NCR_700_HOST_BUSY && SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + DEBUG((" ID %d WARNING: RESELECTION OF BUSY HOST, saving cmd %p, slot %p, addr %x [%04x], resume %x!\n", id, hostdata->cmd, slot, dsp, dsp - hostdata->pScript, resume_offset)); + + switch(dsp - hostdata->pScript) { + case Ent_Disconnect1: + case Ent_Disconnect2: + save_for_reselection(hostdata, SCp, Ent_Disconnect2 + hostdata->pScript); + break; + case Ent_Disconnect3: + case Ent_Disconnect4: + save_for_reselection(hostdata, SCp, Ent_Disconnect4 + hostdata->pScript); + break; + case Ent_Disconnect5: + case Ent_Disconnect6: + save_for_reselection(hostdata, SCp, Ent_Disconnect6 + hostdata->pScript); + break; + case Ent_Disconnect7: + case Ent_Disconnect8: + save_for_reselection(hostdata, SCp, Ent_Disconnect8 + hostdata->pScript); + break; + case Ent_Finish1: + case Ent_Finish2: + process_script_interrupt(A_GOOD_STATUS_AFTER_STATUS, dsp, SCp, host, hostdata); + break; + + default: + slot->state = NCR_700_SLOT_QUEUED; + break; + } + } + hostdata->state = NCR_700_HOST_BUSY; + hostdata->cmd = NULL; + hostdata->msgin[1] = 0; + dma_cache_wback((unsigned long)hostdata->msgin, sizeof(hostdata->msgin)); + + if(id == 0xff) { + /* Selected as target, Ignore */ + resume_offset = hostdata->pScript + Ent_SelectedAsTarget; + } else if(hostdata->tag_negotiated & (1<<id)) { + resume_offset = hostdata->pScript + Ent_GetReselectionWithTag; + } else { + resume_offset = hostdata->pScript + Ent_GetReselectionData; + } + return resume_offset; +} + + +STATIC int +NCR_700_start_command(Scsi_Cmnd *SCp) +{ + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + unsigned long flags; + __u16 count = 1; /* for IDENTIFY message */ + + save_flags(flags); + cli(); + if(hostdata->state != NCR_700_HOST_FREE) { + /* keep this inside the lock to close the race window where + * the running command finishes on another CPU while we don't + * change the state to queued on this one */ + slot->state = NCR_700_SLOT_QUEUED; + restore_flags(flags); + + DEBUG(("scsi%d: host busy, queueing command %p, slot %p\n", + SCp->host->host_no, slot->cmnd, slot)); + return 0; + } + hostdata->state = NCR_700_HOST_BUSY; + hostdata->cmd = SCp; + slot->state = NCR_700_SLOT_BUSY; + /* keep interrupts disabled until we have the command correctly + * set up so we cannot take a selection interrupt */ + + hostdata->msgout[0] = NCR_700_identify(SCp->cmnd[0] != REQUEST_SENSE, + SCp->lun); + /* for INQUIRY or REQUEST_SENSE commands, we cannot be sure + * if the negotiated transfer parameters still hold, so + * always renegotiate them */ + if(SCp->cmnd[0] == INQUIRY || SCp->cmnd[0] == REQUEST_SENSE) { + NCR_700_clear_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + } + + /* REQUEST_SENSE is asking for contingent I_T_L status. If a + * contingent allegiance condition exists, the device will + * refuse all tags, so send the request sense as untagged */ + if((hostdata->tag_negotiated & (1<<SCp->target)) + && (slot->tag != NCR_700_NO_TAG && SCp->cmnd[0] != REQUEST_SENSE)) { + hostdata->msgout[count++] = A_SIMPLE_TAG_MSG; + hostdata->msgout[count++] = slot->tag; + } + + if(hostdata->fast && + NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC)) { + memcpy(&hostdata->msgout[count], NCR_700_SDTR_msg, + sizeof(NCR_700_SDTR_msg)); + count += sizeof(NCR_700_SDTR_msg); + NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + } + + dma_cache_wback((unsigned long)hostdata->msgout, count); + + script_patch_16(hostdata->script, MessageCount, count); + + + script_patch_ID(hostdata->script, + Device_ID, 1<<SCp->target); + + script_patch_32_abs(hostdata->script, CommandAddress, + virt_to_bus(SCp->cmnd)); + script_patch_16(hostdata->script, CommandCount, SCp->cmd_len); + /* finally plumb the beginning of the SG list into the script + * */ + script_patch_32_abs(hostdata->script, SGScriptStartAddress, + virt_to_bus(&slot->SG[0].ins)); + NCR_700_writeb(CLR_FIFO, SCp->host, DFIFO_REG); + + /* set the synchronous period/offset */ + if(slot->resume_offset == 0) + slot->resume_offset = hostdata->pScript; + NCR_700_writeb(NCR_700_get_SXFER(SCp->device), + SCp->host, SXFER_REG); + /* allow interrupts here so that if we're selected we can take + * a selection interrupt. The script start may not be + * effective in this case, but the selection interrupt will + * save our command in that case */ + NCR_700_writel(slot->temp, SCp->host, TEMP_REG); + NCR_700_writel(slot->resume_offset, SCp->host, DSP_REG); + restore_flags(flags); + + return 1; +} + +void +NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct Scsi_Host *host = (struct Scsi_Host *)dev_id; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + __u8 istat; + __u32 resume_offset = 0; + __u8 pun = 0xff, lun = 0xff; + unsigned long flags; + + /* Unfortunately, we have to take the io_request_lock here + * rather than the host lock hostdata->lock because we're + * looking to exclude queuecommand from messing with the + * registers while we're processing the interrupt. Since + * queuecommand is called holding io_request_lock, and we have + * to take io_request_lock before we call the command + * scsi_done, we would get a deadlock if we took + * hostdata->lock here and in queuecommand (because the order + * of locking in queuecommand: 1) io_request_lock then 2) + * hostdata->lock would be the reverse of taking it in this + * routine */ + spin_lock_irqsave(&io_request_lock, flags); + if((istat = NCR_700_readb(host, ISTAT_REG)) + & (SCSI_INT_PENDING | DMA_INT_PENDING)) { + __u32 dsps; + __u8 sstat0 = 0, dstat = 0; + __u32 dsp; + Scsi_Cmnd *SCp = hostdata->cmd; + enum NCR_700_Host_State state; + + state = hostdata->state; + SCp = hostdata->cmd; + + if(istat & SCSI_INT_PENDING) { + udelay(10); + + sstat0 = NCR_700_readb(host, SSTAT0_REG); + } + + if(istat & DMA_INT_PENDING) { + udelay(10); + + dstat = NCR_700_readb(host, DSTAT_REG); + } + + dsps = NCR_700_readl(host, DSPS_REG); + dsp = NCR_700_readl(host, DSP_REG); + + DEBUG(("scsi%d: istat %02x sstat0 %02x dstat %02x dsp %04x[%08x] dsps 0x%x\n", + host->host_no, istat, sstat0, dstat, + (dsp - (__u32)virt_to_bus(hostdata->script))/4, + dsp, dsps)); + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + if(sstat0 & SCSI_RESET_DETECTED) { + Scsi_Device *SDp; + int i; + + hostdata->state = NCR_700_HOST_BUSY; + + printk(KERN_ERR "scsi%d: Bus Reset detected, executing command %p, slot %p, dsp %p[%04x]\n", + host->host_no, SCp, SCp == NULL ? NULL : SCp->host_scribble, (void *)dsp, dsp - hostdata->pScript); + + /* clear all the negotiated parameters */ + for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) + SDp->hostdata = 0; + + /* clear all the slots and their pending commands */ + for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + Scsi_Cmnd *SCp; + struct NCR_700_command_slot *slot = + &hostdata->slots[i]; + + if(slot->state == NCR_700_SLOT_FREE) + continue; + + SCp = slot->cmnd; + printk(KERN_ERR " failing command because of reset, slot %p, cmnd %p\n", + slot, SCp); + free_slot(slot, hostdata); + SCp->host_scribble = NULL; + NCR_700_set_depth(SCp->device, 0); + /* NOTE: deadlock potential here: we + * rely on mid-layer guarantees that + * scsi_done won't try to issue the + * command again otherwise we'll + * deadlock on the + * hostdata->state_lock */ + SCp->result = DID_RESET << 16; + SCp->scsi_done(SCp); + } + mdelay(25); + NCR_700_chip_setup(host); + + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + goto out_unlock; + } else if(sstat0 & SELECTION_TIMEOUT) { + DEBUG(("scsi%d: (%d:%d) selection timeout\n", + host->host_no, pun, lun)); + NCR_700_scsi_done(hostdata, SCp, DID_NO_CONNECT<<16); + } else if(sstat0 & PHASE_MISMATCH) { + struct NCR_700_command_slot *slot = (SCp == NULL) ? NULL : + (struct NCR_700_command_slot *)SCp->host_scribble; + + if(dsp == Ent_SendMessage + 8 + hostdata->pScript) { + /* It wants to reply to some part of + * our message */ +#ifdef NCR_700_DEBUG + __u32 temp = NCR_700_readl(host, TEMP_REG); + int count = (hostdata->script[Ent_SendMessage/4] & 0xffffff) - ((NCR_700_readl(host, DBC_REG) & 0xffffff) + NCR_700_data_residual(host)); + printk("scsi%d (%d:%d) PHASE MISMATCH IN SEND MESSAGE %d remain, return %p[%04x], phase %s\n", host->host_no, pun, lun, count, (void *)temp, temp - hostdata->pScript, sbcl_to_string(NCR_700_readb(host, SBCL_REG))); +#endif + resume_offset = hostdata->pScript + Ent_SendMessagePhaseMismatch; + } else if(dsp >= virt_to_bus(&slot->SG[0].ins) && + dsp <= virt_to_bus(&slot->SG[NCR_700_SG_SEGMENTS].ins)) { + int data_transfer = NCR_700_readl(host, DBC_REG) & 0xffffff; + int SGcount = (dsp - virt_to_bus(&slot->SG[0].ins))/sizeof(struct NCR_700_SG_List); + int residual = NCR_700_data_residual(host); + int i; +#ifdef NCR_700_DEBUG + printk("scsi%d: (%d:%d) Expected phase mismatch in slot->SG[%d], transferred 0x%x\n", + host->host_no, pun, lun, + SGcount, data_transfer); + print_command(SCp->cmnd); + if(residual) { + printk("scsi%d: (%d:%d) Expected phase mismatch in slot->SG[%d], transferred 0x%x, residual %d\n", + host->host_no, pun, lun, + SGcount, data_transfer, residual); + } +#endif + data_transfer += residual; + + if(data_transfer != 0) { + int count; + __u32 pAddr; + + SGcount--; + + count = (bS_to_cpu(slot->SG[SGcount].ins) & 0x00ffffff); + DEBUG(("DATA TRANSFER MISMATCH, count = %d, transferred %d\n", count, count-data_transfer)); + slot->SG[SGcount].ins &= bS_to_host(0xff000000); + slot->SG[SGcount].ins |= bS_to_host(data_transfer); + pAddr = bS_to_cpu(slot->SG[SGcount].pAddr); + pAddr += (count - data_transfer); + slot->SG[SGcount].pAddr = bS_to_host(pAddr); + } + /* set the executed moves to nops */ + for(i=0; i<SGcount; i++) { + slot->SG[i].ins = bS_to_host(SCRIPT_NOP); + slot->SG[i].pAddr = 0; + } + dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG)); + /* and pretend we disconnected after + * the command phase */ + resume_offset = hostdata->pScript + Ent_MsgInDuringData; + } else { + __u8 sbcl = NCR_700_readb(host, SBCL_REG); + printk(KERN_ERR "scsi%d: (%d:%d) phase mismatch at %04x, phase %s\n", + host->host_no, pun, lun, dsp - hostdata->pScript, sbcl_to_string(sbcl)); + NCR_700_internal_bus_reset(host); + } + + } else if(sstat0 & SCSI_GROSS_ERROR) { + printk(KERN_ERR "scsi%d: (%d:%d) GROSS ERROR\n", + host->host_no, pun, lun); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } else if(dstat & SCRIPT_INT_RECEIVED) { + DEBUG(("scsi%d: (%d:%d) ====>SCRIPT INTERRUPT<====\n", + host->host_no, pun, lun)); + resume_offset = process_script_interrupt(dsps, dsp, SCp, host, hostdata); + } else if(dstat & (ILGL_INST_DETECTED)) { + printk(KERN_ERR "scsi%d: (%d:%d) Illegal Instruction detected at 0x%p[0x%x]!!!\n" + " Please email James.Bottomley@HansenPartnership.com with the details\n", + host->host_no, pun, lun, + (void *)dsp, dsp - hostdata->pScript); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } else if(dstat & (WATCH_DOG_INTERRUPT|ABORTED)) { + printk(KERN_ERR "scsi%d: (%d:%d) serious DMA problem, dstat=%02x\n", + host->host_no, pun, lun, dstat); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } + + + /* NOTE: selection interrupt processing MUST occur + * after script interrupt processing to correctly cope + * with the case where we process a disconnect and + * then get reselected before we process the + * disconnection */ + if(sstat0 & SELECTED) { + /* FIXME: It currently takes at least FOUR + * interrupts to complete a command that + * disconnects: one for the disconnect, one + * for the reselection, one to get the + * reselection data and one to complete the + * command. If we guess the reselected + * command here and prepare it, we only need + * to get a reselection data interrupt if we + * guessed wrongly. Since the interrupt + * overhead is much greater than the command + * setup, this would be an efficient + * optimisation particularly as we probably + * only have one outstanding command on a + * target most of the time */ + + resume_offset = process_selection(host, dsp); + + } + + } + + if(resume_offset) { + if(hostdata->state != NCR_700_HOST_BUSY) { + printk(KERN_ERR "scsi%d: Driver error: resume at %p [%04x] with non busy host!\n", + host->host_no, (void *)resume_offset, resume_offset - hostdata->pScript); + hostdata->state = NCR_700_HOST_BUSY; + } + + DEBUG(("Attempting to resume at %x\n", resume_offset)); + NCR_700_writeb(CLR_FIFO, host, DFIFO_REG); + NCR_700_writel(resume_offset, host, DSP_REG); + } + /* There is probably a technical no-no about this: If we're a + * shared interrupt and we got this interrupt because the + * other device needs servicing not us, we're still going to + * check our queued commands here---of course, there shouldn't + * be any outstanding.... */ + if(hostdata->state == NCR_700_HOST_FREE) { + int i; + + for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + /* fairness: always run the queue from the last + * position we left off */ + int j = (i + hostdata->saved_slot_position) + % NCR_700_COMMAND_SLOTS_PER_HOST; + + if(hostdata->slots[j].state != NCR_700_SLOT_QUEUED) + continue; + if(NCR_700_start_command(hostdata->slots[j].cmnd)) { + DEBUG(("scsi%d: Issuing saved command slot %p, cmd %p\t\n", + host->host_no, &hostdata->slots[j], + hostdata->slots[j].cmnd)); + hostdata->saved_slot_position = j + 1; + } + + break; + } + } + out_unlock: + spin_unlock_irqrestore(&io_request_lock, flags); +} + +/* FIXME: Need to put some proc information in and plumb it + * into the scsi proc system */ +STATIC int +NCR_700_proc_directory_info(char *proc_buf, char **startp, + off_t offset, int bytes_available, + int host_no, int write) +{ + static char buf[4096]; /* 1 page should be sufficient */ + int len = 0; + struct Scsi_Host *host = scsi_hostlist; + struct NCR_700_Host_Parameters *hostdata; + Scsi_Device *SDp; + + while(host != NULL && host->host_no != host_no) + host = host->next; + + if(host == NULL) + return 0; + + if(write) { + /* FIXME: Clear internal statistics here */ + return 0; + } + hostdata = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + len += sprintf(&buf[len], "Total commands outstanding: %d\n", hostdata->command_slot_count); + len += sprintf(&buf[len],"\ +Target Depth Active Next Tag\n\ +====== ===== ====== ========\n"); + for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) { + len += sprintf(&buf[len]," %2d:%2d %4d %4d %4d\n", SDp->id, SDp->lun, SDp->queue_depth, NCR_700_get_depth(SDp), SDp->current_tag); + } + if((len -= offset) <= 0) + return 0; + if(len > bytes_available) + len = bytes_available; + memcpy(proc_buf, buf + offset, len); + return len; +} + +STATIC int +NCR_700_queuecommand(Scsi_Cmnd *SCp, void (*done)(Scsi_Cmnd *)) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + __u32 move_ins; + struct NCR_700_command_slot *slot; + int hash; + + if(hostdata->command_slot_count >= NCR_700_COMMAND_SLOTS_PER_HOST) { + /* We're over our allocation, this should never happen + * since we report the max allocation to the mid layer */ + printk(KERN_WARNING "scsi%d: Command depth has gone over queue depth\n", SCp->host->host_no); + return 1; + } + if(NCR_700_get_depth(SCp->device) != 0 && !(hostdata->tag_negotiated & (1<<SCp->target))) { + DEBUG((KERN_ERR "scsi%d (%d:%d) has non zero depth %d\n", + SCp->host->host_no, SCp->target, SCp->lun, + NCR_700_get_depth(SCp->device))); + return 1; + } + if(NCR_700_get_depth(SCp->device) >= NCR_700_MAX_TAGS) { + DEBUG((KERN_ERR "scsi%d (%d:%d) has max tag depth %d\n", + SCp->host->host_no, SCp->target, SCp->lun, + NCR_700_get_depth(SCp->device))); + return 1; + } + NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) + 1); + + /* begin the command here */ + /* no need to check for NULL, test for command_slot_cound above + * ensures a slot is free */ + slot = find_empty_slot(hostdata); + + slot->cmnd = SCp; + + SCp->scsi_done = done; + SCp->host_scribble = (unsigned char *)slot; + SCp->SCp.ptr = NULL; + SCp->SCp.buffer = NULL; + +#ifdef NCR_700_DEBUG + printk("53c700: scsi%d, command ", SCp->host->host_no); + print_command(SCp->cmnd); +#endif + + if(hostdata->tag_negotiated &(1<<SCp->target)) { + + struct NCR_700_command_slot *old = + find_ITL_Nexus(hostdata, SCp->target, SCp->lun); +#ifdef NCR_700_TAG_DEBUG + struct NCR_700_command_slot *found; +#endif + + if(old != NULL && old->tag == SCp->device->current_tag) { + printk(KERN_WARNING "scsi%d (%d:%d) Tag clock back to current, queueing\n", SCp->host->host_no, SCp->target, SCp->lun); + return 1; + } + slot->tag = SCp->device->current_tag++; +#ifdef NCR_700_TAG_DEBUG + while((found = find_ITLQ_Nexus(hostdata, SCp->target, SCp->lun, slot->tag)) != NULL) { + printk("\n\n**ERROR** already using tag %d, but oldest is %d\n", slot->tag, (old == NULL) ? -1 : old->tag); + printk(" FOUND = %p, tag = %d, pun = %d, lun = %d\n", + found, found->tag, found->cmnd->target, found->cmnd->lun); + slot->tag = SCp->device->current_tag++; + printk(" Tag list is: "); + while(old != NULL) { + if(old->cmnd->target == SCp->target && + old->cmnd->lun == SCp->lun) + printk("%d ", old->tag); + old = old->ITL_back; + } + printk("\n\n"); + } +#endif + hash = hash_ITLQ(SCp->target, SCp->lun, slot->tag); + /* link into the ITLQ hash queues */ + slot->ITLQ_forw = hostdata->ITLQ_Hash_forw[hash]; + hostdata->ITLQ_Hash_forw[hash] = slot; +#ifdef NCR_700_TAG_DEBUG + if(slot->ITLQ_forw != NULL && slot->ITLQ_forw->ITLQ_back != NULL) { + printk(KERN_ERR "scsi%d (%d:%d) ITLQ_back is not NULL!!!!\n", SCp->host->host_no, SCp->target, SCp->lun); + } +#endif + if(slot->ITLQ_forw != NULL) + slot->ITLQ_forw->ITLQ_back = slot; + else + hostdata->ITLQ_Hash_back[hash] = slot; + slot->ITLQ_back = NULL; + } else { + slot->tag = NCR_700_NO_TAG; + } + /* link into the ITL hash queues */ + hash = hash_ITL(SCp->target, SCp->lun); + slot->ITL_forw = hostdata->ITL_Hash_forw[hash]; + hostdata->ITL_Hash_forw[hash] = slot; +#ifdef NCR_700_TAG_DEBUG + if(slot->ITL_forw != NULL && slot->ITL_forw->ITL_back != NULL) { + printk(KERN_ERR "scsi%d (%d:%d) ITL_back is not NULL!!!!\n", + SCp->host->host_no, SCp->target, SCp->lun); + } +#endif + if(slot->ITL_forw != NULL) + slot->ITL_forw->ITL_back = slot; + else + hostdata->ITL_Hash_back[hash] = slot; + slot->ITL_back = NULL; + + + /* This is f****g ridiculous; every low level HBA driver has + * to determine the direction of the commands, why isn't this + * done inside the scsi_lib !!??? */ + switch (SCp->cmnd[0]) { + case REQUEST_SENSE: + /* clear the internal sense magic */ + SCp->cmnd[6] = 0; + /* fall through */ + case INQUIRY: + case MODE_SENSE: + case READ_6: + case READ_10: + case READ_12: + case READ_CAPACITY: + case READ_BLOCK_LIMITS: + case READ_TOC: + move_ins = SCRIPT_MOVE_DATA_IN; + break; + case MODE_SELECT: + case WRITE_6: + case WRITE_10: + case WRITE_12: + move_ins = SCRIPT_MOVE_DATA_OUT; + break; + case TEST_UNIT_READY: + case ALLOW_MEDIUM_REMOVAL: + case START_STOP: + move_ins = 0; + break; + default: + /* OK, get it from the command */ + switch(SCp->sc_data_direction) { + case SCSI_DATA_UNKNOWN: + default: + printk(KERN_ERR "53c700: Unknown command for data direction "); + print_command(SCp->cmnd); + + move_ins = 0; + break; + case SCSI_DATA_NONE: + move_ins = 0; + break; + case SCSI_DATA_READ: + move_ins = SCRIPT_MOVE_DATA_IN; + break; + case SCSI_DATA_WRITE: + move_ins = SCRIPT_MOVE_DATA_OUT; + break; + } + } + + /* now build the scatter gather list */ + if(move_ins != 0) { + int i; + + for(i = 0; i < (SCp->use_sg ? SCp->use_sg : 1); i++) { + void *vPtr; + __u32 count; + + if(SCp->use_sg) { + vPtr = (((struct scatterlist *)SCp->buffer)[i].address); + count = ((struct scatterlist *)SCp->buffer)[i].length; + } else { + vPtr = SCp->request_buffer; + count = SCp->request_bufflen; + } + slot->SG[i].ins = bS_to_host(move_ins | count); + DEBUG((" scatter block %d: move %d[%08x] from 0x%lx\n", + i, count, slot->SG[i].ins, + virt_to_bus(vPtr))); + dma_cache_wback_inv((unsigned long)vPtr, count); + slot->SG[i].pAddr = bS_to_host(virt_to_bus(vPtr)); + } + slot->SG[i].ins = bS_to_host(SCRIPT_RETURN); + slot->SG[i].pAddr = 0; + dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG)); + DEBUG((" SETTING %08lx to %x\n", + virt_to_bus(&slot->SG[i].ins), + slot->SG[i].ins)); + } + slot->resume_offset = 0; + NCR_700_start_command(SCp); + return 0; +} + +STATIC int +NCR_700_abort(Scsi_Cmnd * SCp) +{ + struct NCR_700_command_slot *slot; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + + printk(KERN_INFO "scsi%d (%d:%d) New error handler wants to abort command\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + slot = find_ITL_Nexus(hostdata, SCp->target, SCp->lun); + while(slot != NULL && slot->cmnd != SCp) + slot = slot->ITL_back; + + if(slot == NULL) + /* no outstanding command to abort */ + return SUCCESS; + if(SCp->cmnd[0] == TEST_UNIT_READY) { + /* FIXME: This is because of a problem in the new + * error handler. When it is in error recovery, it + * will send a TUR to a device it thinks may still be + * showing a problem. If the TUR isn't responded to, + * it will abort it and mark the device off line. + * Unfortunately, it does no other error recovery, so + * this would leave us with an outstanding command + * occupying a slot. Rather than allow this to + * happen, we issue a bus reset to force all + * outstanding commands to terminate here. */ + NCR_700_internal_bus_reset(SCp->host); + /* still drop through and return failed */ + } + return FAILED; + +} + +STATIC int +NCR_700_bus_reset(Scsi_Cmnd * SCp) +{ + printk(KERN_INFO "scsi%d (%d:%d) New error handler wants BUS reset, cmd %p\n\t", + SCp->host->host_no, SCp->target, SCp->lun, SCp); + print_command(SCp->cmnd); + NCR_700_internal_bus_reset(SCp->host); + return SUCCESS; +} + +STATIC int +NCR_700_dev_reset(Scsi_Cmnd * SCp) +{ + printk(KERN_INFO "scsi%d (%d:%d) New error handler wants device reset\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + return FAILED; +} + +STATIC int +NCR_700_host_reset(Scsi_Cmnd * SCp) +{ + printk(KERN_INFO "scsi%d (%d:%d) New error handler wants HOST reset\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + NCR_700_internal_bus_reset(SCp->host); + NCR_700_chip_reset(SCp->host); + return SUCCESS; +} + +EXPORT_SYMBOL(NCR_700_detect); +EXPORT_SYMBOL(NCR_700_release); +EXPORT_SYMBOL(NCR_700_intr); diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c new file mode 100644 index 000000000000..ea77fd0d541d --- /dev/null +++ b/drivers/scsi/53c700.c @@ -0,0 +1,1840 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR (or Symbios) 53c700 and 53c700-66 Driver + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com +**----------------------------------------------------------------------------- +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +**----------------------------------------------------------------------------- + */ + +/* Notes: + * + * This driver is designed exclusively for these chips (virtually the + * earliest of the scripts engine chips). They need their own drivers + * because they are missing so many of the scripts and snazzy register + * features of their elder brothers (the 710, 720 and 770). + * + * The 700 is the lowliest of the line, it can only do async SCSI. + * The 700-66 can at least do synchronous SCSI up to 10MHz. + * + * The 700 chip has no host bus interface logic of its own. However, + * it is usually mapped to a location with well defined register + * offsets. Therefore, if you can determine the base address and the + * irq your board incorporating this chip uses, you can probably use + * this driver to run it (although you'll probably have to write a + * minimal wrapper for the purpose---see the NCR_D700 driver for + * details about how to do this). + * + * + * TODO List: + * + * 1. Better statistics in the proc fs + * + * 2. Implement message queue (queues SCSI messages like commands) and make + * the abort and device reset functions use them. + * */ + +/* CHANGELOG + * + * Version 2.3 + * + * More endianness/cache coherency changes. + * + * Better bad device handling (handles devices lying about tag + * queueing support and devices which fail to provide sense data on + * contingent allegiance conditions) + * + * Many thanks to Richard Hirst <rhirst@linuxcare.com> for patiently + * debugging this driver on the parisc architecture and suggesting + * many improvements and bug fixes. + * + * Thanks also go to Linuxcare Inc. for providing several PARISC + * machines for me to debug the driver on. + * + * Version 2.2 + * + * Made the driver mem or io mapped; added endian invariance; added + * dma cache flushing operations for architectures which need it; + * added support for more varied clocking speeds. + * + * Version 2.1 + * + * Initial modularisation from the D700. See NCR_D700.c for the rest of + * the changelog. + * */ +#define NCR_700_VERSION "2.3" + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/mca.h> +#include <asm/dma.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/byteorder.h> +#include <linux/blk.h> +#include <linux/module.h> + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#include "53c700.h" + +#ifdef NCR_700_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("53c700 and 53c700-66 Driver"); +MODULE_LICENSE("GPL"); + +/* This is the script */ +#include "53c700_d.h" + + +STATIC int NCR_700_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +STATIC int NCR_700_abort(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_bus_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_dev_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_host_reset(Scsi_Cmnd * SCpnt); +STATIC int NCR_700_proc_directory_info(char *, char **, off_t, int, int, int); +STATIC void NCR_700_chip_setup(struct Scsi_Host *host); +STATIC void NCR_700_chip_reset(struct Scsi_Host *host); + +static char *NCR_700_phase[] = { + "", + "after selection", + "before command phase", + "after command phase", + "after status phase", + "after data in phase", + "after data out phase", + "during data phase", +}; + +static char *NCR_700_condition[] = { + "", + "NOT MSG_OUT", + "UNEXPECTED PHASE", + "NOT MSG_IN", + "UNEXPECTED MSG", + "MSG_IN", + "SDTR_MSG RECEIVED", + "REJECT_MSG RECEIVED", + "DISCONNECT_MSG RECEIVED", + "MSG_OUT", + "DATA_IN", + +}; + +static char *NCR_700_fatal_messages[] = { + "unexpected message after reselection", + "still MSG_OUT after message injection", + "not MSG_IN after selection", + "Illegal message length received", +}; + +static char *NCR_700_SBCL_bits[] = { + "IO ", + "CD ", + "MSG ", + "ATN ", + "SEL ", + "BSY ", + "ACK ", + "REQ ", +}; + +static char *NCR_700_SBCL_to_phase[] = { + "DATA_OUT", + "DATA_IN", + "CMD_OUT", + "STATE", + "ILLEGAL PHASE", + "ILLEGAL PHASE", + "MSG OUT", + "MSG IN", +}; + +static __u8 NCR_700_SDTR_msg[] = { + 0x01, /* Extended message */ + 0x03, /* Extended message Length */ + 0x01, /* SDTR Extended message */ + NCR_700_MIN_PERIOD, + NCR_700_MAX_OFFSET +}; + +struct Scsi_Host * __init +NCR_700_detect(Scsi_Host_Template *tpnt, + struct NCR_700_Host_Parameters *hostdata) +{ + __u32 *script = kmalloc(sizeof(SCRIPT), GFP_KERNEL); + __u32 pScript; + struct Scsi_Host *host; + static int banner = 0; + int j; + + /* Fill in the missing routines from the host template */ + tpnt->queuecommand = NCR_700_queuecommand; + tpnt->eh_abort_handler = NCR_700_abort; + tpnt->eh_device_reset_handler = NCR_700_dev_reset; + tpnt->eh_bus_reset_handler = NCR_700_bus_reset; + tpnt->eh_host_reset_handler = NCR_700_host_reset; + tpnt->can_queue = NCR_700_COMMAND_SLOTS_PER_HOST; + tpnt->sg_tablesize = NCR_700_SG_SEGMENTS; + tpnt->cmd_per_lun = NCR_700_MAX_TAGS; + tpnt->use_clustering = DISABLE_CLUSTERING; + tpnt->use_new_eh_code = 1; + tpnt->proc_info = NCR_700_proc_directory_info; + + if(tpnt->name == NULL) + tpnt->name = "53c700"; + if(tpnt->proc_name == NULL) + tpnt->proc_name = "53c700"; + + + if((host = scsi_register(tpnt, 4)) == NULL) + return NULL; + if(script == NULL) { + printk(KERN_ERR "53c700: Failed to allocate script, detatching\n"); + scsi_unregister(host); + return NULL; + } + + hostdata->slots = kmalloc(sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST, GFP_KERNEL); + if(hostdata->slots == NULL) { + printk(KERN_ERR "53c700: Failed to allocate command slots, detatching\n"); + scsi_unregister(host); + return NULL; + } + memset(hostdata->slots, 0, sizeof(struct NCR_700_command_slot) * NCR_700_COMMAND_SLOTS_PER_HOST); + for(j = 0; j < NCR_700_COMMAND_SLOTS_PER_HOST; j++) { + if(j == 0) + hostdata->free_list = &hostdata->slots[j]; + else + hostdata->slots[j-1].ITL_forw = &hostdata->slots[j]; + hostdata->slots[j].state = NCR_700_SLOT_FREE; + } + host->hostdata[0] = (__u32)hostdata; + for(j = 0; j < sizeof(SCRIPT)/sizeof(SCRIPT[0]); j++) { + script[j] = bS_to_host(SCRIPT[j]); + } + /* bus physical address of script */ + pScript = virt_to_bus(script); + /* adjust all labels to be bus physical */ + for(j = 0; j < PATCHES; j++) { + script[LABELPATCHES[j]] = bS_to_host(pScript + SCRIPT[LABELPATCHES[j]]); + } + /* now patch up fixed addresses */ + script_patch_32(script, MessageLocation, + virt_to_bus(&hostdata->msgout[0])); + script_patch_32(script, StatusAddress, + virt_to_bus(&hostdata->status)); + script_patch_32(script, ReceiveMsgAddress, + virt_to_bus(&hostdata->msgin[0])); + + hostdata->script = script; + hostdata->pScript = pScript; + hostdata->state = NCR_700_HOST_FREE; + spin_lock_init(&hostdata->lock); + hostdata->cmd = NULL; + host->max_id = 7; + host->max_lun = NCR_700_MAX_LUNS; + host->unique_id = hostdata->base; + host->base = hostdata->base; + host->hostdata[0] = (unsigned long)hostdata; + /* kick the chip */ + NCR_700_writeb(0xff, host, CTEST9_REG); + hostdata->rev = (NCR_700_readb(host, CTEST7_REG)<<4) & 0x0f; + hostdata->fast = (NCR_700_readb(host, CTEST9_REG) == 0); + if(banner == 0) { + printk(KERN_NOTICE "53c700: Version " NCR_700_VERSION " By James.Bottomley@HansenPartnership.com\n"); + banner = 1; + } + printk(KERN_NOTICE "scsi%d: %s rev %d %s\n", host->host_no, + hostdata->fast ? "53c700-66" : "53c700", + hostdata->rev, hostdata->differential ? + "(Differential)" : ""); + /* reset the chip */ + NCR_700_chip_reset(host); + NCR_700_writeb(ASYNC_OPERATION , host, SXFER_REG); + + return host; +} + +int +NCR_700_release(struct Scsi_Host *host) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + kfree(hostdata->script); + return 1; +} + +static inline __u8 +NCR_700_identify(int can_disconnect, __u8 lun) +{ + return IDENTIFY_BASE | + ((can_disconnect) ? 0x40 : 0) | + (lun & NCR_700_LUN_MASK); +} + +/* + * Function : static int datapath_residual (Scsi_Host *host) + * + * Purpose : return residual data count of what's in the chip. If you + * really want to know what this function is doing, it's almost a + * direct transcription of the algorithm described in the 53c710 + * guide, except that the DBC and DFIFO registers are only 6 bits + * wide. + * + * Inputs : host - SCSI host */ +static inline int +NCR_700_data_residual (struct Scsi_Host *host) { + int count, synchronous; + unsigned int ddir; + + count = ((NCR_700_readb(host, DFIFO_REG) & 0x3f) - + (NCR_700_readl(host, DBC_REG) & 0x3f)) & 0x3f; + + synchronous = NCR_700_readb(host, SXFER_REG) & 0x0f; + + /* get the data direction */ + ddir = NCR_700_readb(host, CTEST0_REG) & 0x01; + + if (ddir) { + /* Receive */ + if (synchronous) + count += (NCR_700_readb(host, SSTAT2_REG) & 0xf0) >> 4; + else + if (NCR_700_readb(host, SSTAT1_REG) & SIDL_REG_FULL) + ++count; + } else { + /* Send */ + __u8 sstat = NCR_700_readb(host, SSTAT1_REG); + if (sstat & SODL_REG_FULL) + ++count; + if (synchronous && (sstat & SODR_REG_FULL)) + ++count; + } + return count; +} + +/* print out the SCSI wires and corresponding phase from the SBCL register + * in the chip */ +static inline char * +sbcl_to_string(__u8 sbcl) +{ + int i; + static char ret[256]; + + ret[0]='\0'; + for(i=0; i<8; i++) { + if((1<<i) & sbcl) + strcat(ret, NCR_700_SBCL_bits[i]); + } + strcat(ret, NCR_700_SBCL_to_phase[sbcl & 0x07]); + return ret; +} + +static inline __u8 +bitmap_to_number(__u8 bitmap) +{ + __u8 i; + + for(i=0; i<8 && !(bitmap &(1<<i)); i++) + ; + return i; +} + +/* Pull a slot off the free list */ +STATIC struct NCR_700_command_slot * +find_empty_slot(struct NCR_700_Host_Parameters *hostdata) +{ + struct NCR_700_command_slot *slot = hostdata->free_list; + + if(slot == NULL) { + /* sanity check */ + if(hostdata->command_slot_count != NCR_700_COMMAND_SLOTS_PER_HOST) + printk(KERN_ERR "SLOTS FULL, but count is %d, should be %d\n", hostdata->command_slot_count, NCR_700_COMMAND_SLOTS_PER_HOST); + return NULL; + } + + if(slot->state != NCR_700_SLOT_FREE) + /* should panic! */ + printk(KERN_ERR "BUSY SLOT ON FREE LIST!!!\n"); + + + hostdata->free_list = slot->ITL_forw; + slot->ITL_forw = NULL; + + + /* NOTE: set the state to busy here, not queued, since this + * indicates the slot is in use and cannot be run by the IRQ + * finish routine. If we cannot queue the command when it + * is properly build, we then change to NCR_700_SLOT_QUEUED */ + slot->state = NCR_700_SLOT_BUSY; + hostdata->command_slot_count++; + + return slot; +} + +STATIC void +free_slot(struct NCR_700_command_slot *slot, + struct NCR_700_Host_Parameters *hostdata) +{ + int hash; + struct NCR_700_command_slot **forw, **back; + + + if((slot->state & NCR_700_SLOT_MASK) != NCR_700_SLOT_MAGIC) { + printk(KERN_ERR "53c700: SLOT %p is not MAGIC!!!\n", slot); + } + if(slot->state == NCR_700_SLOT_FREE) { + printk(KERN_ERR "53c700: SLOT %p is FREE!!!\n", slot); + } + /* remove from queues */ + if(slot->tag != NCR_700_NO_TAG) { + hash = hash_ITLQ(slot->cmnd->target, slot->cmnd->lun, + slot->tag); + if(slot->ITLQ_forw == NULL) + back = &hostdata->ITLQ_Hash_back[hash]; + else + back = &slot->ITLQ_forw->ITLQ_back; + + if(slot->ITLQ_back == NULL) + forw = &hostdata->ITLQ_Hash_forw[hash]; + else + forw = &slot->ITLQ_back->ITLQ_forw; + + *forw = slot->ITLQ_forw; + *back = slot->ITLQ_back; + } + hash = hash_ITL(slot->cmnd->target, slot->cmnd->lun); + if(slot->ITL_forw == NULL) + back = &hostdata->ITL_Hash_back[hash]; + else + back = &slot->ITL_forw->ITL_back; + + if(slot->ITL_back == NULL) + forw = &hostdata->ITL_Hash_forw[hash]; + else + forw = &slot->ITL_back->ITL_forw; + + *forw = slot->ITL_forw; + *back = slot->ITL_back; + + slot->resume_offset = 0; + slot->cmnd = NULL; + slot->state = NCR_700_SLOT_FREE; + slot->ITL_forw = hostdata->free_list; + hostdata->free_list = slot; + hostdata->command_slot_count--; +} + + +/* This routine really does very little. The command is indexed on + the ITL and (if tagged) the ITLQ lists in _queuecommand */ +STATIC void +save_for_reselection(struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp) +{ + /* Its just possible that this gets executed twice */ + if(SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + slot->resume_offset = dsp; + } + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; +} + +/* Most likely nexus is the oldest in each case */ +STATIC inline struct NCR_700_command_slot * +find_ITL_Nexus(struct NCR_700_Host_Parameters *hostdata, __u8 pun, __u8 lun) +{ + int hash = hash_ITL(pun, lun); + struct NCR_700_command_slot *slot = hostdata->ITL_Hash_back[hash]; + while(slot != NULL && !(slot->cmnd->target == pun && + slot->cmnd->lun == lun)) + slot = slot->ITL_back; + return slot; +} + +STATIC inline struct NCR_700_command_slot * +find_ITLQ_Nexus(struct NCR_700_Host_Parameters *hostdata, __u8 pun, + __u8 lun, __u8 tag) +{ + int hash = hash_ITLQ(pun, lun, tag); + struct NCR_700_command_slot *slot = hostdata->ITLQ_Hash_back[hash]; + + while(slot != NULL && !(slot->cmnd->target == pun + && slot->cmnd->lun == lun && slot->tag == tag)) + slot = slot->ITLQ_back; + +#ifdef NCR_700_TAG_DEBUG + if(slot != NULL) { + struct NCR_700_command_slot *n = slot->ITLQ_back; + while(n != NULL && n->cmnd->target != pun + && n->cmnd->lun != lun && n->tag != tag) + n = n->ITLQ_back; + + if(n != NULL && n->cmnd->target == pun && n->cmnd->lun == lun + && n->tag == tag) { + printk(KERN_WARNING "53c700: WARNING: DUPLICATE tag %d\n", + tag); + } + } +#endif + return slot; +} + + + +/* This translates the SDTR message offset and period to a value + * which can be loaded into the SXFER_REG. + * + * NOTE: According to SCSI-2, the true transfer period (in ns) is + * actually four times this period value */ +STATIC inline __u8 +NCR_700_offset_period_to_sxfer(struct NCR_700_Host_Parameters *hostdata, + __u8 offset, __u8 period) +{ + int XFERP; + + if(period*4 < NCR_700_MIN_PERIOD) { + printk(KERN_WARNING "53c700: Period %dns is less than SCSI-2 minimum, setting to %d\n", period*4, NCR_700_MIN_PERIOD); + period = NCR_700_MIN_PERIOD/4; + } + XFERP = (period*4 * hostdata->sync_clock)/1000 - 4; + if(offset > NCR_700_MAX_OFFSET) { + printk(KERN_WARNING "53c700: Offset %d exceeds maximum, setting to %d\n", + offset, NCR_700_MAX_OFFSET); + offset = NCR_700_MAX_OFFSET; + } + if(XFERP < NCR_700_MIN_XFERP) { + printk(KERN_WARNING "53c700: XFERP %d is less than minium, setting to %d\n", + XFERP, NCR_700_MIN_XFERP); + XFERP = NCR_700_MIN_XFERP; + } + return (offset & 0x0f) | (XFERP & 0x07)<<4; +} + + +STATIC inline void +NCR_700_scsi_done(struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, int result) +{ + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + + if(SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + + if(SCp->cmnd[0] == REQUEST_SENSE && SCp->cmnd[6] == NCR_700_INTERNAL_SENSE_MAGIC) { +#ifdef NCR_700_DEBUG + printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is", + SCp, SCp->cmnd[7], result); + print_sense("53c700", SCp); +#endif + if(result == 0) + result = SCp->cmnd[7]; + } + + free_slot(slot, hostdata); + + SCp->host_scribble = NULL; + SCp->result = result; + SCp->scsi_done(SCp); + if(NCR_700_get_depth(SCp->device) == 0 || + NCR_700_get_depth(SCp->device) > NCR_700_MAX_TAGS) + printk(KERN_ERR "Invalid depth in NCR_700_scsi_done(): %d\n", + NCR_700_get_depth(SCp->device)); + NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) - 1); + } else { + printk(KERN_ERR "53c700: SCSI DONE HAS NULL SCp\n"); + } +} + + +STATIC void +NCR_700_internal_bus_reset(struct Scsi_Host *host) +{ + /* Bus reset */ + NCR_700_writeb(ASSERT_RST, host, SCNTL1_REG); + udelay(50); + NCR_700_writeb(0, host, SCNTL1_REG); + +} + +STATIC void +NCR_700_chip_setup(struct Scsi_Host *host) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + NCR_700_writeb(1 << host->this_id, host, SCID_REG); + NCR_700_writeb(0, host, SBCL_REG); + NCR_700_writeb(0, host, SXFER_REG); + + NCR_700_writeb(PHASE_MM_INT | SEL_TIMEOUT_INT | GROSS_ERR_INT | UX_DISC_INT + | RST_INT | PAR_ERR_INT | SELECT_INT, host, SIEN_REG); + + NCR_700_writeb(ABORT_INT | INT_INST_INT | ILGL_INST_INT, host, DIEN_REG); + NCR_700_writeb(BURST_LENGTH_8, host, DMODE_REG); + NCR_700_writeb(FULL_ARBITRATION | PARITY | AUTO_ATN, host, SCNTL0_REG); + NCR_700_writeb(LAST_DIS_ENBL | ENABLE_ACTIVE_NEGATION|GENERATE_RECEIVE_PARITY, + host, CTEST8_REG); + NCR_700_writeb(ENABLE_SELECT, host, SCNTL1_REG); + if(hostdata->clock > 75) { + printk(KERN_ERR "53c700: Clock speed %dMHz is too high: 75Mhz is the maximum this chip can be driven at\n", hostdata->clock); + /* do the best we can, but the async clock will be out + * of spec: sync divider 2, async divider 3 */ + DEBUG(("53c700: sync 2 async 3\n")); + NCR_700_writeb(SYNC_DIV_2_0, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_3_0, host, DCNTL_REG); + hostdata->sync_clock = hostdata->clock/2; + } else if(hostdata->clock > 50 && hostdata->clock <= 75) { + /* sync divider 1.5, async divider 3 */ + DEBUG(("53c700: sync 1.5 async 3\n")); + NCR_700_writeb(SYNC_DIV_1_5, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_3_0, host, DCNTL_REG); + hostdata->sync_clock = hostdata->clock*2; + hostdata->sync_clock /= 3; + + } else if(hostdata->clock > 37 && hostdata->clock <= 50) { + /* sync divider 1, async divider 2 */ + DEBUG(("53c700: sync 1 async 2\n")); + NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_2_0, host, DCNTL_REG); + hostdata->sync_clock = hostdata->clock; + } else if(hostdata->clock > 25 && hostdata->clock <=37) { + /* sync divider 1, async divider 1.5 */ + DEBUG(("53c700: sync 1 async 1.5\n")); + NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_1_5, host, DCNTL_REG); + hostdata->sync_clock = hostdata->clock; + } else { + DEBUG(("53c700: sync 1 async 1\n")); + NCR_700_writeb(SYNC_DIV_1_0, host, SBCL_REG); + NCR_700_writeb(ASYNC_DIV_1_0, host, DCNTL_REG); + /* sync divider 1, async divider 1 */ + } +} + +STATIC void +NCR_700_chip_reset(struct Scsi_Host *host) +{ + /* Chip reset */ + NCR_700_writeb(SOFTWARE_RESET, host, DCNTL_REG); + udelay(100); + + NCR_700_writeb(0, host, DCNTL_REG); + + mdelay(1000); + + NCR_700_chip_setup(host); +} + +/* The heart of the message processing engine is that the instruction + * immediately after the INT is the normal case (and so must be CLEAR + * ACK). If we want to do something else, we call that routine in + * scripts and set temp to be the normal case + 8 (skipping the CLEAR + * ACK) so that the routine returns correctly to resume its activity + * */ +STATIC __u32 +process_extended_message(struct Scsi_Host *host, + struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp, __u32 dsps) +{ + __u32 resume_offset = dsp, temp = dsp + 8; + __u8 pun = 0xff, lun = 0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + switch(hostdata->msgin[2]) { + case A_SDTR_MSG: + if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { + __u8 period = hostdata->msgin[3]; + __u8 offset = hostdata->msgin[4]; + __u8 sxfer; + + if(offset != 0 && period != 0) + sxfer = NCR_700_offset_period_to_sxfer(hostdata, offset, period); + else + sxfer = 0; + + if(sxfer != NCR_700_get_SXFER(SCp->device)) { + printk(KERN_INFO "scsi%d: (%d:%d) Synchronous at offset %d, period %dns\n", + host->host_no, pun, lun, + offset, period*4); + + NCR_700_set_SXFER(SCp->device, sxfer); + } + + + NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + + NCR_700_writeb(NCR_700_get_SXFER(SCp->device), + host, SXFER_REG); + + } else { + /* SDTR message out of the blue, reject it */ + printk(KERN_WARNING "scsi%d Unexpected SDTR msg\n", + host->host_no); + hostdata->msgout[0] = A_REJECT_MSG; + dma_cache_wback((unsigned long)hostdata->msgout, sizeof(hostdata->msgout)); + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + } + break; + + case A_WDTR_MSG: + printk(KERN_INFO "scsi%d: (%d:%d), Unsolicited WDTR after CMD, Rejecting\n", + host->host_no, pun, lun); + hostdata->msgout[0] = A_REJECT_MSG; + dma_cache_wback((unsigned long)hostdata->msgout, sizeof(hostdata->msgout)); + script_patch_16(hostdata->script, MessageCount, 1); + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + + break; + + default: + printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + print_msg(hostdata->msgin); + printk("\n"); + /* just reject it */ + hostdata->msgout[0] = A_REJECT_MSG; + dma_cache_wback((unsigned long)hostdata->msgout, sizeof(hostdata->msgout)); + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + } + NCR_700_writel(temp, host, TEMP_REG); + return resume_offset; +} + +STATIC __u32 +process_message(struct Scsi_Host *host, struct NCR_700_Host_Parameters *hostdata, + Scsi_Cmnd *SCp, __u32 dsp, __u32 dsps) +{ + /* work out where to return to */ + __u32 temp = dsp + 8, resume_offset = dsp; + __u8 pun = 0xff, lun = 0xff; + + dma_cache_inv((unsigned long)hostdata->msgin, sizeof(hostdata->msgin)); + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + +#ifdef NCR_700_DEBUG + printk("scsi%d (%d:%d): message %s: ", host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + print_msg(hostdata->msgin); + printk("\n"); +#endif + + switch(hostdata->msgin[0]) { + + case A_EXTENDED_MSG: + return process_extended_message(host, hostdata, SCp, + dsp, dsps); + + case A_REJECT_MSG: + if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { + /* Rejected our sync negotiation attempt */ + NCR_700_set_SXFER(SCp->device, 0); + NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + } else if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING)) { + /* rejected our first simple tag message */ + printk(KERN_WARNING "scsi%d (%d:%d) Rejected first tag queue attempt, turning off tag queueing\n", host->host_no, pun, lun); + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + hostdata->tag_negotiated &= ~(1<<SCp->target); + } else { + printk(KERN_WARNING "scsi%d (%d:%d) Unexpected REJECT Message %s\n", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + /* however, just ignore it */ + } + break; + + case A_PARITY_ERROR_MSG: + printk(KERN_ERR "scsi%d (%d:%d) Parity Error!\n", host->host_no, + pun, lun); + NCR_700_internal_bus_reset(host); + break; + case A_SIMPLE_TAG_MSG: + printk(KERN_INFO "scsi%d (%d:%d) SIMPLE TAG %d %s\n", host->host_no, + pun, lun, hostdata->msgin[1], + NCR_700_phase[(dsps & 0xf00) >> 8]); + /* just ignore it */ + break; + default: + printk(KERN_INFO "scsi%d (%d:%d): Unexpected message %s: ", + host->host_no, pun, lun, + NCR_700_phase[(dsps & 0xf00) >> 8]); + + print_msg(hostdata->msgin); + printk("\n"); + /* just reject it */ + hostdata->msgout[0] = A_REJECT_MSG; + dma_cache_wback((unsigned long)hostdata->msgout, sizeof(hostdata->msgout)); + script_patch_16(hostdata->script, MessageCount, 1); + /* SendMsgOut returns, so set up the return + * address */ + resume_offset = hostdata->pScript + Ent_SendMessageWithATN; + + break; + } + NCR_700_writel(temp, host, TEMP_REG); + return resume_offset; +} + +STATIC __u32 +process_script_interrupt(__u32 dsps, __u32 dsp, Scsi_Cmnd *SCp, + struct Scsi_Host *host, + struct NCR_700_Host_Parameters *hostdata) +{ + __u32 resume_offset = 0; + __u8 pun = 0xff, lun=0xff; + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + if(dsps == A_GOOD_STATUS_AFTER_STATUS) { + dma_cache_inv((unsigned long)hostdata->status, sizeof(hostdata->status)); + DEBUG((" COMMAND COMPLETE, status=%02x\n", + hostdata->status)); + /* OK, if TCQ still on, we know it works */ + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + /* check for contingent allegiance contitions */ + if(status_byte(hostdata->status) == CHECK_CONDITION || + status_byte(hostdata->status) == COMMAND_TERMINATED) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + if(SCp->cmnd[0] == REQUEST_SENSE) { + /* OOPS: bad device, returning another + * contingent allegiance condition */ + printk(KERN_ERR "scsi%d (%d:%d) broken device is looping in contingent allegiance: ignoring\n", host->host_no, pun, lun); + NCR_700_scsi_done(hostdata, SCp, hostdata->status); + } else { + + DEBUG((" cmd %p has status %d, requesting sense\n", + SCp, hostdata->status)); + /* we can destroy the command here because the + * contingent allegiance condition will cause a + * retry which will re-copy the command from the + * saved data_cmnd */ + SCp->cmnd[0] = REQUEST_SENSE; + SCp->cmnd[1] = (SCp->lun & 0x7) << 5; + SCp->cmnd[2] = 0; + SCp->cmnd[3] = 0; + SCp->cmnd[4] = sizeof(SCp->sense_buffer); + SCp->cmnd[5] = 0; + SCp->cmd_len = 6; + /* Here's a quiet hack: the REQUEST_SENSE command is + * six bytes, so store a flag indicating that this + * was an internal sense request and the original + * status at the end of the command */ + SCp->cmnd[6] = NCR_700_INTERNAL_SENSE_MAGIC; + SCp->cmnd[7] = hostdata->status; + slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer)); + slot->SG[0].pAddr = bS_to_host(virt_to_bus(SCp->sense_buffer)); + slot->SG[1].ins = bS_to_host(SCRIPT_RETURN); + slot->SG[1].pAddr = 0; + slot->resume_offset = hostdata->pScript; + dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG[0])*2); + dma_cache_inv((unsigned long)SCp->sense_buffer, sizeof(SCp->sense_buffer)); + + /* queue the command for reissue */ + slot->state = NCR_700_SLOT_QUEUED; + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + } + } else { + if(status_byte(hostdata->status) == GOOD && + SCp->cmnd[0] == INQUIRY && SCp->use_sg == 0) { + /* Piggy back the tag queueing support + * on this command */ + if(((char *)SCp->request_buffer)[7] & 0x02) { + printk(KERN_INFO "scsi%d: (%d:%d) Enabling Tag Command Queuing\n", host->host_no, pun, lun); + hostdata->tag_negotiated |= (1<<SCp->target); + NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + } else { + NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING); + hostdata->tag_negotiated &= ~(1<<SCp->target); + } + } + NCR_700_scsi_done(hostdata, SCp, hostdata->status); + } + } else if((dsps & 0xfffff0f0) == A_UNEXPECTED_PHASE) { + __u8 i = (dsps & 0xf00) >> 8; + + printk(KERN_ERR "scsi%d: (%d:%d), UNEXPECTED PHASE %s (%s)\n", + host->host_no, pun, lun, + NCR_700_phase[i], + sbcl_to_string(NCR_700_readb(host, SBCL_REG))); + printk(KERN_ERR " len = %d, cmd =", SCp->cmd_len); + print_command(SCp->cmnd); + + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff000) == A_FATAL) { + int i = (dsps & 0xfff); + + printk(KERN_ERR "scsi%d: (%d:%d) FATAL ERROR: %s\n", + host->host_no, pun, lun, NCR_700_fatal_messages[i]); + if(dsps == A_FATAL_ILLEGAL_MSG_LENGTH) { + printk(KERN_ERR " msg begins %02x %02x\n", + hostdata->msgin[0], hostdata->msgin[1]); + } + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff0f0) == A_DISCONNECT) { +#ifdef NCR_700_DEBUG + __u8 i = (dsps & 0xf00) >> 8; + + printk("scsi%d: (%d:%d), DISCONNECTED (%d) %s\n", + host->host_no, pun, lun, + i, NCR_700_phase[i]); +#endif + save_for_reselection(hostdata, SCp, dsp); + + } else if(dsps == A_RESELECTION_IDENTIFIED) { + __u8 lun; + struct NCR_700_command_slot *slot; + __u8 reselection_id = hostdata->reselection_id; + + dma_cache_inv((unsigned long)hostdata->msgin, sizeof(hostdata->msgin)); + + lun = hostdata->msgin[0] & 0x1f; + + hostdata->reselection_id = 0xff; + DEBUG(("scsi%d: (%d:%d) RESELECTED!\n", + host->host_no, reselection_id, lun)); + /* clear the reselection indicator */ + if(hostdata->msgin[1] == A_SIMPLE_TAG_MSG) { + slot = find_ITLQ_Nexus(hostdata, reselection_id, + lun, hostdata->msgin[2]); + } else { + slot = find_ITL_Nexus(hostdata, reselection_id, lun); + } + retry: + if(slot == NULL) { + struct NCR_700_command_slot *s = find_ITL_Nexus(hostdata, reselection_id, lun); + printk(KERN_ERR "scsi%d: (%d:%d) RESELECTED but no saved command (MSG = %02x %02x %02x)!!\n", + host->host_no, reselection_id, lun, + hostdata->msgin[0], hostdata->msgin[1], + hostdata->msgin[2]); + printk(KERN_ERR " OUTSTANDING TAGS:"); + while(s != NULL) { + if(s->cmnd->target == reselection_id && + s->cmnd->lun == lun) { + printk("%d ", s->tag); + if(s->tag == hostdata->msgin[2]) { + printk(" ***FOUND*** \n"); + slot = s; + goto retry; + } + + } + s = s->ITL_back; + } + printk("\n"); + } else { + if(hostdata->state != NCR_700_HOST_BUSY) + printk(KERN_ERR "scsi%d: FATAL, host not busy during valid reselection!\n", + host->host_no); + resume_offset = slot->resume_offset; + hostdata->cmd = slot->cmnd; + + /* re-patch for this command */ + script_patch_32_abs(hostdata->script, CommandAddress, + virt_to_bus(slot->cmnd->cmnd)); + script_patch_16(hostdata->script, + CommandCount, slot->cmnd->cmd_len); + script_patch_32_abs(hostdata->script, SGScriptStartAddress, + virt_to_bus(&slot->SG[0].ins)); + + /* Note: setting SXFER only works if we're + * still in the MESSAGE phase, so it is vital + * that ACK is still asserted when we process + * the reselection message. The resume offset + * should therefore always clear ACK */ + NCR_700_writeb(NCR_700_get_SXFER(hostdata->cmd->device), + host, SXFER_REG); + + } + } else if(dsps == A_RESELECTED_DURING_SELECTION) { + + /* This section is full of debugging code because I've + * never managed to reach it. I think what happens is + * that, because the 700 runs with selection + * interrupts enabled the whole time that we take a + * selection interrupt before we manage to get to the + * reselected script interrupt */ + + __u8 reselection_id = NCR_700_readb(host, SFBR_REG); + struct NCR_700_command_slot *slot; + + /* Take out our own ID */ + reselection_id &= ~(1<<host->this_id); + + printk(KERN_INFO "scsi%d: (%d:%d) RESELECTION DURING SELECTION, dsp=%p[%04x] state=%d, count=%d\n", + host->host_no, reselection_id, lun, (void *)dsp, dsp - hostdata->pScript, hostdata->state, hostdata->command_slot_count); + + { + /* FIXME: DEBUGGING CODE */ + __u32 SG = (__u32)bus_to_virt(hostdata->script[A_SGScriptStartAddress_used[0]]); + int i; + + for(i=0; i< NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + if(SG >= (__u32)(&hostdata->slots[i].SG[0]) + && SG <= (__u32)(&hostdata->slots[i].SG[NCR_700_SG_SEGMENTS])) + break; + } + printk(KERN_INFO "IDENTIFIED SG segment as being %p in slot %p, cmd %p, slot->resume_offset=%p\n", (void *)SG, &hostdata->slots[i], hostdata->slots[i].cmnd, (void *)hostdata->slots[i].resume_offset); + SCp = hostdata->slots[i].cmnd; + } + + if(SCp != NULL) { + slot = (struct NCR_700_command_slot *)SCp->host_scribble; + /* change slot from busy to queued to redo command */ + slot->state = NCR_700_SLOT_QUEUED; + } + hostdata->cmd = NULL; + + if(reselection_id == 0) { + if(hostdata->reselection_id == 0xff) { + printk(KERN_ERR "scsi%d: Invalid reselection during selection!!\n", host->host_no); + return 0; + } else { + printk(KERN_ERR "scsi%d: script reselected and we took a selection interrupt\n", + host->host_no); + reselection_id = hostdata->reselection_id; + } + } else { + + /* convert to real ID */ + reselection_id = bitmap_to_number(reselection_id); + } + hostdata->reselection_id = reselection_id; + hostdata->msgin[1] = 0; + dma_cache_wback((unsigned long)hostdata->msgin, sizeof(hostdata->msgin)); + if(hostdata->tag_negotiated & (1<<reselection_id)) { + resume_offset = hostdata->pScript + Ent_GetReselectionWithTag; + } else { + resume_offset = hostdata->pScript + Ent_GetReselectionData; + } + } else if(dsps == A_COMPLETED_SELECTION_AS_TARGET) { + /* we've just disconnected from the bus, do nothing since + * a return here will re-run the queued command slot + * that may have been interrupted by the initial selection */ + DEBUG((" SELECTION COMPLETED\n")); + } else if((dsps & 0xfffff0f0) == A_MSG_IN) { + resume_offset = process_message(host, hostdata, SCp, + dsp, dsps); + } else if((dsps & 0xfffff000) == 0) { + __u8 i = (dsps & 0xf0) >> 4, j = (dsps & 0xf00) >> 8; + printk(KERN_ERR "scsi%d: (%d:%d), unhandled script condition %s %s at %04x\n", + host->host_no, pun, lun, NCR_700_condition[i], + NCR_700_phase[j], dsp - hostdata->pScript); + if(SCp != NULL) { + print_command(SCp->cmnd); + + if(SCp->use_sg) { + for(i = 0; i < SCp->use_sg + 1; i++) { + printk(KERN_INFO " SG[%d].length = %d, move_insn=%08x, addr %08x\n", i, ((struct scatterlist *)SCp->buffer)[i].length, ((struct NCR_700_command_slot *)SCp->host_scribble)->SG[i].ins, ((struct NCR_700_command_slot *)SCp->host_scribble)->SG[i].pAddr); + } + } + } + NCR_700_internal_bus_reset(host); + } else if((dsps & 0xfffff000) == A_DEBUG_INTERRUPT) { + printk(KERN_NOTICE "scsi%d (%d:%d) DEBUG INTERRUPT %d AT %p[%04x], continuing\n", + host->host_no, pun, lun, dsps & 0xfff, (void *)dsp, dsp - hostdata->pScript); + resume_offset = dsp; + } else { + printk(KERN_ERR "scsi%d: (%d:%d), unidentified script interrupt 0x%x at %04x\n", + host->host_no, pun, lun, dsps, dsp - hostdata->pScript); + NCR_700_internal_bus_reset(host); + } + return resume_offset; +} + +/* We run the 53c700 with selection interrupts always enabled. This + * means that the chip may be selected as soon as the bus frees. On a + * busy bus, this can be before the scripts engine finishes its + * processing. Therefore, part of the selection processing has to be + * to find out what the scripts engine is doing and complete the + * function if necessary (i.e. process the pending disconnect or save + * the interrupted initial selection */ +STATIC inline __u32 +process_selection(struct Scsi_Host *host, __u32 dsp) +{ + __u8 id = 0; /* Squash compiler warning */ + int count = 0; + __u32 resume_offset = 0; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + Scsi_Cmnd *SCp = hostdata->cmd; + __u8 sbcl; + + for(count = 0; count < 5; count++) { + id = NCR_700_readb(host, SFBR_REG); + + /* Take out our own ID */ + id &= ~(1<<host->this_id); + if(id != 0) + break; + udelay(5); + } + sbcl = NCR_700_readb(host, SBCL_REG); + if((sbcl & SBCL_IO) == 0) { + /* mark as having been selected rather than reselected */ + id = 0xff; + } else { + /* convert to real ID */ + hostdata->reselection_id = id = bitmap_to_number(id); + DEBUG(("scsi%d: Reselected by %d\n", + host->host_no, id)); + } + if(hostdata->state == NCR_700_HOST_BUSY && SCp != NULL) { + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + DEBUG((" ID %d WARNING: RESELECTION OF BUSY HOST, saving cmd %p, slot %p, addr %x [%04x], resume %x!\n", id, hostdata->cmd, slot, dsp, dsp - hostdata->pScript, resume_offset)); + + switch(dsp - hostdata->pScript) { + case Ent_Disconnect1: + case Ent_Disconnect2: + save_for_reselection(hostdata, SCp, Ent_Disconnect2 + hostdata->pScript); + break; + case Ent_Disconnect3: + case Ent_Disconnect4: + save_for_reselection(hostdata, SCp, Ent_Disconnect4 + hostdata->pScript); + break; + case Ent_Disconnect5: + case Ent_Disconnect6: + save_for_reselection(hostdata, SCp, Ent_Disconnect6 + hostdata->pScript); + break; + case Ent_Disconnect7: + case Ent_Disconnect8: + save_for_reselection(hostdata, SCp, Ent_Disconnect8 + hostdata->pScript); + break; + case Ent_Finish1: + case Ent_Finish2: + process_script_interrupt(A_GOOD_STATUS_AFTER_STATUS, dsp, SCp, host, hostdata); + break; + + default: + slot->state = NCR_700_SLOT_QUEUED; + break; + } + } + hostdata->state = NCR_700_HOST_BUSY; + hostdata->cmd = NULL; + hostdata->msgin[1] = 0; + dma_cache_wback((unsigned long)hostdata->msgin, sizeof(hostdata->msgin)); + + if(id == 0xff) { + /* Selected as target, Ignore */ + resume_offset = hostdata->pScript + Ent_SelectedAsTarget; + } else if(hostdata->tag_negotiated & (1<<id)) { + resume_offset = hostdata->pScript + Ent_GetReselectionWithTag; + } else { + resume_offset = hostdata->pScript + Ent_GetReselectionData; + } + return resume_offset; +} + + +STATIC int +NCR_700_start_command(Scsi_Cmnd *SCp) +{ + struct NCR_700_command_slot *slot = + (struct NCR_700_command_slot *)SCp->host_scribble; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + unsigned long flags; + __u16 count = 1; /* for IDENTIFY message */ + + save_flags(flags); + cli(); + if(hostdata->state != NCR_700_HOST_FREE) { + /* keep this inside the lock to close the race window where + * the running command finishes on another CPU while we don't + * change the state to queued on this one */ + slot->state = NCR_700_SLOT_QUEUED; + restore_flags(flags); + + DEBUG(("scsi%d: host busy, queueing command %p, slot %p\n", + SCp->host->host_no, slot->cmnd, slot)); + return 0; + } + hostdata->state = NCR_700_HOST_BUSY; + hostdata->cmd = SCp; + slot->state = NCR_700_SLOT_BUSY; + /* keep interrupts disabled until we have the command correctly + * set up so we cannot take a selection interrupt */ + + hostdata->msgout[0] = NCR_700_identify(SCp->cmnd[0] != REQUEST_SENSE, + SCp->lun); + /* for INQUIRY or REQUEST_SENSE commands, we cannot be sure + * if the negotiated transfer parameters still hold, so + * always renegotiate them */ + if(SCp->cmnd[0] == INQUIRY || SCp->cmnd[0] == REQUEST_SENSE) { + NCR_700_clear_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); + } + + /* REQUEST_SENSE is asking for contingent I_T_L status. If a + * contingent allegiance condition exists, the device will + * refuse all tags, so send the request sense as untagged */ + if((hostdata->tag_negotiated & (1<<SCp->target)) + && (slot->tag != NCR_700_NO_TAG && SCp->cmnd[0] != REQUEST_SENSE)) { + hostdata->msgout[count++] = A_SIMPLE_TAG_MSG; + hostdata->msgout[count++] = slot->tag; + } + + if(hostdata->fast && + NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC)) { + memcpy(&hostdata->msgout[count], NCR_700_SDTR_msg, + sizeof(NCR_700_SDTR_msg)); + count += sizeof(NCR_700_SDTR_msg); + NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + } + + dma_cache_wback((unsigned long)hostdata->msgout, count); + + script_patch_16(hostdata->script, MessageCount, count); + + + script_patch_ID(hostdata->script, + Device_ID, 1<<SCp->target); + + script_patch_32_abs(hostdata->script, CommandAddress, + virt_to_bus(SCp->cmnd)); + script_patch_16(hostdata->script, CommandCount, SCp->cmd_len); + /* finally plumb the beginning of the SG list into the script + * */ + script_patch_32_abs(hostdata->script, SGScriptStartAddress, + virt_to_bus(&slot->SG[0].ins)); + NCR_700_writeb(CLR_FIFO, SCp->host, DFIFO_REG); + + /* set the synchronous period/offset */ + if(slot->resume_offset == 0) + slot->resume_offset = hostdata->pScript; + NCR_700_writeb(NCR_700_get_SXFER(SCp->device), + SCp->host, SXFER_REG); + /* allow interrupts here so that if we're selected we can take + * a selection interrupt. The script start may not be + * effective in this case, but the selection interrupt will + * save our command in that case */ + NCR_700_writel(slot->temp, SCp->host, TEMP_REG); + NCR_700_writel(slot->resume_offset, SCp->host, DSP_REG); + restore_flags(flags); + + return 1; +} + +void +NCR_700_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct Scsi_Host *host = (struct Scsi_Host *)dev_id; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)host->hostdata[0]; + __u8 istat; + __u32 resume_offset = 0; + __u8 pun = 0xff, lun = 0xff; + unsigned long flags; + + /* Unfortunately, we have to take the io_request_lock here + * rather than the host lock hostdata->lock because we're + * looking to exclude queuecommand from messing with the + * registers while we're processing the interrupt. Since + * queuecommand is called holding io_request_lock, and we have + * to take io_request_lock before we call the command + * scsi_done, we would get a deadlock if we took + * hostdata->lock here and in queuecommand (because the order + * of locking in queuecommand: 1) io_request_lock then 2) + * hostdata->lock would be the reverse of taking it in this + * routine */ + spin_lock_irqsave(&io_request_lock, flags); + if((istat = NCR_700_readb(host, ISTAT_REG)) + & (SCSI_INT_PENDING | DMA_INT_PENDING)) { + __u32 dsps; + __u8 sstat0 = 0, dstat = 0; + __u32 dsp; + Scsi_Cmnd *SCp = hostdata->cmd; + enum NCR_700_Host_State state; + + state = hostdata->state; + SCp = hostdata->cmd; + + if(istat & SCSI_INT_PENDING) { + udelay(10); + + sstat0 = NCR_700_readb(host, SSTAT0_REG); + } + + if(istat & DMA_INT_PENDING) { + udelay(10); + + dstat = NCR_700_readb(host, DSTAT_REG); + } + + dsps = NCR_700_readl(host, DSPS_REG); + dsp = NCR_700_readl(host, DSP_REG); + + DEBUG(("scsi%d: istat %02x sstat0 %02x dstat %02x dsp %04x[%08x] dsps 0x%x\n", + host->host_no, istat, sstat0, dstat, + (dsp - (__u32)virt_to_bus(hostdata->script))/4, + dsp, dsps)); + + if(SCp != NULL) { + pun = SCp->target; + lun = SCp->lun; + } + + if(sstat0 & SCSI_RESET_DETECTED) { + Scsi_Device *SDp; + int i; + + hostdata->state = NCR_700_HOST_BUSY; + + printk(KERN_ERR "scsi%d: Bus Reset detected, executing command %p, slot %p, dsp %p[%04x]\n", + host->host_no, SCp, SCp == NULL ? NULL : SCp->host_scribble, (void *)dsp, dsp - hostdata->pScript); + + /* clear all the negotiated parameters */ + for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) + SDp->hostdata = 0; + + /* clear all the slots and their pending commands */ + for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + Scsi_Cmnd *SCp; + struct NCR_700_command_slot *slot = + &hostdata->slots[i]; + + if(slot->state == NCR_700_SLOT_FREE) + continue; + + SCp = slot->cmnd; + printk(KERN_ERR " failing command because of reset, slot %p, cmnd %p\n", + slot, SCp); + free_slot(slot, hostdata); + SCp->host_scribble = NULL; + NCR_700_set_depth(SCp->device, 0); + /* NOTE: deadlock potential here: we + * rely on mid-layer guarantees that + * scsi_done won't try to issue the + * command again otherwise we'll + * deadlock on the + * hostdata->state_lock */ + SCp->result = DID_RESET << 16; + SCp->scsi_done(SCp); + } + mdelay(25); + NCR_700_chip_setup(host); + + hostdata->state = NCR_700_HOST_FREE; + hostdata->cmd = NULL; + goto out_unlock; + } else if(sstat0 & SELECTION_TIMEOUT) { + DEBUG(("scsi%d: (%d:%d) selection timeout\n", + host->host_no, pun, lun)); + NCR_700_scsi_done(hostdata, SCp, DID_NO_CONNECT<<16); + } else if(sstat0 & PHASE_MISMATCH) { + struct NCR_700_command_slot *slot = (SCp == NULL) ? NULL : + (struct NCR_700_command_slot *)SCp->host_scribble; + + if(dsp == Ent_SendMessage + 8 + hostdata->pScript) { + /* It wants to reply to some part of + * our message */ +#ifdef NCR_700_DEBUG + __u32 temp = NCR_700_readl(host, TEMP_REG); + int count = (hostdata->script[Ent_SendMessage/4] & 0xffffff) - ((NCR_700_readl(host, DBC_REG) & 0xffffff) + NCR_700_data_residual(host)); + printk("scsi%d (%d:%d) PHASE MISMATCH IN SEND MESSAGE %d remain, return %p[%04x], phase %s\n", host->host_no, pun, lun, count, (void *)temp, temp - hostdata->pScript, sbcl_to_string(NCR_700_readb(host, SBCL_REG))); +#endif + resume_offset = hostdata->pScript + Ent_SendMessagePhaseMismatch; + } else if(dsp >= virt_to_bus(&slot->SG[0].ins) && + dsp <= virt_to_bus(&slot->SG[NCR_700_SG_SEGMENTS].ins)) { + int data_transfer = NCR_700_readl(host, DBC_REG) & 0xffffff; + int SGcount = (dsp - virt_to_bus(&slot->SG[0].ins))/sizeof(struct NCR_700_SG_List); + int residual = NCR_700_data_residual(host); + int i; +#ifdef NCR_700_DEBUG + printk("scsi%d: (%d:%d) Expected phase mismatch in slot->SG[%d], transferred 0x%x\n", + host->host_no, pun, lun, + SGcount, data_transfer); + print_command(SCp->cmnd); + if(residual) { + printk("scsi%d: (%d:%d) Expected phase mismatch in slot->SG[%d], transferred 0x%x, residual %d\n", + host->host_no, pun, lun, + SGcount, data_transfer, residual); + } +#endif + data_transfer += residual; + + if(data_transfer != 0) { + int count; + __u32 pAddr; + + SGcount--; + + count = (bS_to_cpu(slot->SG[SGcount].ins) & 0x00ffffff); + DEBUG(("DATA TRANSFER MISMATCH, count = %d, transferred %d\n", count, count-data_transfer)); + slot->SG[SGcount].ins &= bS_to_host(0xff000000); + slot->SG[SGcount].ins |= bS_to_host(data_transfer); + pAddr = bS_to_cpu(slot->SG[SGcount].pAddr); + pAddr += (count - data_transfer); + slot->SG[SGcount].pAddr = bS_to_host(pAddr); + } + /* set the executed moves to nops */ + for(i=0; i<SGcount; i++) { + slot->SG[i].ins = bS_to_host(SCRIPT_NOP); + slot->SG[i].pAddr = 0; + } + dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG)); + /* and pretend we disconnected after + * the command phase */ + resume_offset = hostdata->pScript + Ent_MsgInDuringData; + } else { + __u8 sbcl = NCR_700_readb(host, SBCL_REG); + printk(KERN_ERR "scsi%d: (%d:%d) phase mismatch at %04x, phase %s\n", + host->host_no, pun, lun, dsp - hostdata->pScript, sbcl_to_string(sbcl)); + NCR_700_internal_bus_reset(host); + } + + } else if(sstat0 & SCSI_GROSS_ERROR) { + printk(KERN_ERR "scsi%d: (%d:%d) GROSS ERROR\n", + host->host_no, pun, lun); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } else if(dstat & SCRIPT_INT_RECEIVED) { + DEBUG(("scsi%d: (%d:%d) ====>SCRIPT INTERRUPT<====\n", + host->host_no, pun, lun)); + resume_offset = process_script_interrupt(dsps, dsp, SCp, host, hostdata); + } else if(dstat & (ILGL_INST_DETECTED)) { + printk(KERN_ERR "scsi%d: (%d:%d) Illegal Instruction detected at 0x%p[0x%x]!!!\n" + " Please email James.Bottomley@HansenPartnership.com with the details\n", + host->host_no, pun, lun, + (void *)dsp, dsp - hostdata->pScript); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } else if(dstat & (WATCH_DOG_INTERRUPT|ABORTED)) { + printk(KERN_ERR "scsi%d: (%d:%d) serious DMA problem, dstat=%02x\n", + host->host_no, pun, lun, dstat); + NCR_700_scsi_done(hostdata, SCp, DID_ERROR<<16); + } + + + /* NOTE: selection interrupt processing MUST occur + * after script interrupt processing to correctly cope + * with the case where we process a disconnect and + * then get reselected before we process the + * disconnection */ + if(sstat0 & SELECTED) { + /* FIXME: It currently takes at least FOUR + * interrupts to complete a command that + * disconnects: one for the disconnect, one + * for the reselection, one to get the + * reselection data and one to complete the + * command. If we guess the reselected + * command here and prepare it, we only need + * to get a reselection data interrupt if we + * guessed wrongly. Since the interrupt + * overhead is much greater than the command + * setup, this would be an efficient + * optimisation particularly as we probably + * only have one outstanding command on a + * target most of the time */ + + resume_offset = process_selection(host, dsp); + + } + + } + + if(resume_offset) { + if(hostdata->state != NCR_700_HOST_BUSY) { + printk(KERN_ERR "scsi%d: Driver error: resume at %p [%04x] with non busy host!\n", + host->host_no, (void *)resume_offset, resume_offset - hostdata->pScript); + hostdata->state = NCR_700_HOST_BUSY; + } + + DEBUG(("Attempting to resume at %x\n", resume_offset)); + NCR_700_writeb(CLR_FIFO, host, DFIFO_REG); + NCR_700_writel(resume_offset, host, DSP_REG); + } + /* There is probably a technical no-no about this: If we're a + * shared interrupt and we got this interrupt because the + * other device needs servicing not us, we're still going to + * check our queued commands here---of course, there shouldn't + * be any outstanding.... */ + if(hostdata->state == NCR_700_HOST_FREE) { + int i; + + for(i = 0; i < NCR_700_COMMAND_SLOTS_PER_HOST; i++) { + /* fairness: always run the queue from the last + * position we left off */ + int j = (i + hostdata->saved_slot_position) + % NCR_700_COMMAND_SLOTS_PER_HOST; + + if(hostdata->slots[j].state != NCR_700_SLOT_QUEUED) + continue; + if(NCR_700_start_command(hostdata->slots[j].cmnd)) { + DEBUG(("scsi%d: Issuing saved command slot %p, cmd %p\t\n", + host->host_no, &hostdata->slots[j], + hostdata->slots[j].cmnd)); + hostdata->saved_slot_position = j + 1; + } + + break; + } + } + out_unlock: + spin_unlock_irqrestore(&io_request_lock, flags); +} + +/* FIXME: Need to put some proc information in and plumb it + * into the scsi proc system */ +STATIC int +NCR_700_proc_directory_info(char *proc_buf, char **startp, + off_t offset, int bytes_available, + int host_no, int write) +{ + static char buf[4096]; /* 1 page should be sufficient */ + int len = 0; + struct Scsi_Host *host = scsi_hostlist; + struct NCR_700_Host_Parameters *hostdata; + Scsi_Device *SDp; + + while(host != NULL && host->host_no != host_no) + host = host->next; + + if(host == NULL) + return 0; + + if(write) { + /* FIXME: Clear internal statistics here */ + return 0; + } + hostdata = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + len += sprintf(&buf[len], "Total commands outstanding: %d\n", hostdata->command_slot_count); + len += sprintf(&buf[len],"\ +Target Depth Active Next Tag\n\ +====== ===== ====== ========\n"); + for(SDp = host->host_queue; SDp != NULL; SDp = SDp->next) { + len += sprintf(&buf[len]," %2d:%2d %4d %4d %4d\n", SDp->id, SDp->lun, SDp->queue_depth, NCR_700_get_depth(SDp), SDp->current_tag); + } + if((len -= offset) <= 0) + return 0; + if(len > bytes_available) + len = bytes_available; + memcpy(proc_buf, buf + offset, len); + return len; +} + +STATIC int +NCR_700_queuecommand(Scsi_Cmnd *SCp, void (*done)(Scsi_Cmnd *)) +{ + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + __u32 move_ins; + struct NCR_700_command_slot *slot; + int hash; + + if(hostdata->command_slot_count >= NCR_700_COMMAND_SLOTS_PER_HOST) { + /* We're over our allocation, this should never happen + * since we report the max allocation to the mid layer */ + printk(KERN_WARNING "scsi%d: Command depth has gone over queue depth\n", SCp->host->host_no); + return 1; + } + if(NCR_700_get_depth(SCp->device) != 0 && !(hostdata->tag_negotiated & (1<<SCp->target))) { + DEBUG((KERN_ERR "scsi%d (%d:%d) has non zero depth %d\n", + SCp->host->host_no, SCp->target, SCp->lun, + NCR_700_get_depth(SCp->device))); + return 1; + } + if(NCR_700_get_depth(SCp->device) >= NCR_700_MAX_TAGS) { + DEBUG((KERN_ERR "scsi%d (%d:%d) has max tag depth %d\n", + SCp->host->host_no, SCp->target, SCp->lun, + NCR_700_get_depth(SCp->device))); + return 1; + } + NCR_700_set_depth(SCp->device, NCR_700_get_depth(SCp->device) + 1); + + /* begin the command here */ + /* no need to check for NULL, test for command_slot_cound above + * ensures a slot is free */ + slot = find_empty_slot(hostdata); + + slot->cmnd = SCp; + + SCp->scsi_done = done; + SCp->host_scribble = (unsigned char *)slot; + SCp->SCp.ptr = NULL; + SCp->SCp.buffer = NULL; + +#ifdef NCR_700_DEBUG + printk("53c700: scsi%d, command ", SCp->host->host_no); + print_command(SCp->cmnd); +#endif + + if(hostdata->tag_negotiated &(1<<SCp->target)) { + + struct NCR_700_command_slot *old = + find_ITL_Nexus(hostdata, SCp->target, SCp->lun); +#ifdef NCR_700_TAG_DEBUG + struct NCR_700_command_slot *found; +#endif + + if(old != NULL && old->tag == SCp->device->current_tag) { + printk(KERN_WARNING "scsi%d (%d:%d) Tag clock back to current, queueing\n", SCp->host->host_no, SCp->target, SCp->lun); + return 1; + } + slot->tag = SCp->device->current_tag++; +#ifdef NCR_700_TAG_DEBUG + while((found = find_ITLQ_Nexus(hostdata, SCp->target, SCp->lun, slot->tag)) != NULL) { + printk("\n\n**ERROR** already using tag %d, but oldest is %d\n", slot->tag, (old == NULL) ? -1 : old->tag); + printk(" FOUND = %p, tag = %d, pun = %d, lun = %d\n", + found, found->tag, found->cmnd->target, found->cmnd->lun); + slot->tag = SCp->device->current_tag++; + printk(" Tag list is: "); + while(old != NULL) { + if(old->cmnd->target == SCp->target && + old->cmnd->lun == SCp->lun) + printk("%d ", old->tag); + old = old->ITL_back; + } + printk("\n\n"); + } +#endif + hash = hash_ITLQ(SCp->target, SCp->lun, slot->tag); + /* link into the ITLQ hash queues */ + slot->ITLQ_forw = hostdata->ITLQ_Hash_forw[hash]; + hostdata->ITLQ_Hash_forw[hash] = slot; +#ifdef NCR_700_TAG_DEBUG + if(slot->ITLQ_forw != NULL && slot->ITLQ_forw->ITLQ_back != NULL) { + printk(KERN_ERR "scsi%d (%d:%d) ITLQ_back is not NULL!!!!\n", SCp->host->host_no, SCp->target, SCp->lun); + } +#endif + if(slot->ITLQ_forw != NULL) + slot->ITLQ_forw->ITLQ_back = slot; + else + hostdata->ITLQ_Hash_back[hash] = slot; + slot->ITLQ_back = NULL; + } else { + slot->tag = NCR_700_NO_TAG; + } + /* link into the ITL hash queues */ + hash = hash_ITL(SCp->target, SCp->lun); + slot->ITL_forw = hostdata->ITL_Hash_forw[hash]; + hostdata->ITL_Hash_forw[hash] = slot; +#ifdef NCR_700_TAG_DEBUG + if(slot->ITL_forw != NULL && slot->ITL_forw->ITL_back != NULL) { + printk(KERN_ERR "scsi%d (%d:%d) ITL_back is not NULL!!!!\n", + SCp->host->host_no, SCp->target, SCp->lun); + } +#endif + if(slot->ITL_forw != NULL) + slot->ITL_forw->ITL_back = slot; + else + hostdata->ITL_Hash_back[hash] = slot; + slot->ITL_back = NULL; + + + /* This is f****g ridiculous; every low level HBA driver has + * to determine the direction of the commands, why isn't this + * done inside the scsi_lib !!??? */ + switch (SCp->cmnd[0]) { + case REQUEST_SENSE: + /* clear the internal sense magic */ + SCp->cmnd[6] = 0; + /* fall through */ + case INQUIRY: + case MODE_SENSE: + case READ_6: + case READ_10: + case READ_12: + case READ_CAPACITY: + case READ_BLOCK_LIMITS: + case READ_TOC: + move_ins = SCRIPT_MOVE_DATA_IN; + break; + case MODE_SELECT: + case WRITE_6: + case WRITE_10: + case WRITE_12: + move_ins = SCRIPT_MOVE_DATA_OUT; + break; + case TEST_UNIT_READY: + case ALLOW_MEDIUM_REMOVAL: + case START_STOP: + move_ins = 0; + break; + default: + /* OK, get it from the command */ + switch(SCp->sc_data_direction) { + case SCSI_DATA_UNKNOWN: + default: + printk(KERN_ERR "53c700: Unknown command for data direction "); + print_command(SCp->cmnd); + + move_ins = 0; + break; + case SCSI_DATA_NONE: + move_ins = 0; + break; + case SCSI_DATA_READ: + move_ins = SCRIPT_MOVE_DATA_IN; + break; + case SCSI_DATA_WRITE: + move_ins = SCRIPT_MOVE_DATA_OUT; + break; + } + } + + /* now build the scatter gather list */ + if(move_ins != 0) { + int i; + + for(i = 0; i < (SCp->use_sg ? SCp->use_sg : 1); i++) { + void *vPtr; + __u32 count; + + if(SCp->use_sg) { + vPtr = (((struct scatterlist *)SCp->buffer)[i].address); + count = ((struct scatterlist *)SCp->buffer)[i].length; + } else { + vPtr = SCp->request_buffer; + count = SCp->request_bufflen; + } + slot->SG[i].ins = bS_to_host(move_ins | count); + DEBUG((" scatter block %d: move %d[%08x] from 0x%lx\n", + i, count, slot->SG[i].ins, + virt_to_bus(vPtr))); + dma_cache_wback_inv((unsigned long)vPtr, count); + slot->SG[i].pAddr = bS_to_host(virt_to_bus(vPtr)); + } + slot->SG[i].ins = bS_to_host(SCRIPT_RETURN); + slot->SG[i].pAddr = 0; + dma_cache_wback((unsigned long)slot->SG, sizeof(slot->SG)); + DEBUG((" SETTING %08lx to %x\n", + virt_to_bus(&slot->SG[i].ins), + slot->SG[i].ins)); + } + slot->resume_offset = 0; + NCR_700_start_command(SCp); + return 0; +} + +STATIC int +NCR_700_abort(Scsi_Cmnd * SCp) +{ + struct NCR_700_command_slot *slot; + struct NCR_700_Host_Parameters *hostdata = + (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; + + printk(KERN_INFO "scsi%d (%d:%d) New error handler wants to abort command\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + slot = find_ITL_Nexus(hostdata, SCp->target, SCp->lun); + while(slot != NULL && slot->cmnd != SCp) + slot = slot->ITL_back; + + if(slot == NULL) + /* no outstanding command to abort */ + return SUCCESS; + if(SCp->cmnd[0] == TEST_UNIT_READY) { + /* FIXME: This is because of a problem in the new + * error handler. When it is in error recovery, it + * will send a TUR to a device it thinks may still be + * showing a problem. If the TUR isn't responded to, + * it will abort it and mark the device off line. + * Unfortunately, it does no other error recovery, so + * this would leave us with an outstanding command + * occupying a slot. Rather than allow this to + * happen, we issue a bus reset to force all + * outstanding commands to terminate here. */ + NCR_700_internal_bus_reset(SCp->host); + /* still drop through and return failed */ + } + return FAILED; + +} + +STATIC int +NCR_700_bus_reset(Scsi_Cmnd * SCp) +{ + printk(KERN_INFO "scsi%d (%d:%d) New error handler wants BUS reset, cmd %p\n\t", + SCp->host->host_no, SCp->target, SCp->lun, SCp); + print_command(SCp->cmnd); + NCR_700_internal_bus_reset(SCp->host); + return SUCCESS; +} + +STATIC int +NCR_700_dev_reset(Scsi_Cmnd * SCp) +{ + printk(KERN_INFO "scsi%d (%d:%d) New error handler wants device reset\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + return FAILED; +} + +STATIC int +NCR_700_host_reset(Scsi_Cmnd * SCp) +{ + printk(KERN_INFO "scsi%d (%d:%d) New error handler wants HOST reset\n\t", + SCp->host->host_no, SCp->target, SCp->lun); + print_command(SCp->cmnd); + + NCR_700_internal_bus_reset(SCp->host); + NCR_700_chip_reset(SCp->host); + return SUCCESS; +} + +EXPORT_SYMBOL(NCR_700_detect); +EXPORT_SYMBOL(NCR_700_release); +EXPORT_SYMBOL(NCR_700_intr); diff --git a/drivers/scsi/53c700.h b/drivers/scsi/53c700.h new file mode 100644 index 000000000000..456b78b43328 --- /dev/null +++ b/drivers/scsi/53c700.h @@ -0,0 +1,534 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* Driver for 53c700 and 53c700-66 chips from NCR and Symbios + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com + */ + +#ifndef _53C700_H +#define _53C700_H + +/* Turn on for general debugging---too verbose for normal use */ +#undef NCR_700_DEBUG +/* Debug the tag queues, checking hash queue allocation and deallocation + * and search for duplicate tags */ +#undef NCR_700_TAG_DEBUG + +#ifdef NCR_700_DEBUG +#define DEBUG(x) printk x +#else +#define DEBUG(x) +#endif + +/* The number of available command slots */ +#define NCR_700_COMMAND_SLOTS_PER_HOST 64 +/* The maximum number of Scatter Gathers we allow */ +#define NCR_700_SG_SEGMENTS 32 +/* The maximum number of luns (make this of the form 2^n) */ +#define NCR_700_MAX_LUNS 32 +#define NCR_700_LUN_MASK (NCR_700_MAX_LUNS - 1) +/* Alter this with care: too many tags won't give the elevator a chance to + * work; too few will cause the device to operate less efficiently */ +#define NCR_700_MAX_TAGS 16 +/* magic byte identifying an internally generated REQUEST_SENSE command */ +#define NCR_700_INTERNAL_SENSE_MAGIC 0x42 + +/* WARNING: Leave this in for now: the dependency preprocessor doesn't + * pick up file specific flags, so must define here if they are not + * set */ +#if !defined(IO_MAPPED) && !defined(MEM_MAPPED) +#define IO_MAPPED +#endif + + +struct NCR_700_Host_Parameters; + +/* These are the externally used routines */ +struct Scsi_Host *NCR_700_detect(Scsi_Host_Template *, struct NCR_700_Host_Parameters *); +int NCR_700_release(struct Scsi_Host *host); +void NCR_700_intr(int, void *, struct pt_regs *); + + +enum NCR_700_Host_State { + NCR_700_HOST_BUSY, + NCR_700_HOST_FREE, +}; + +struct NCR_700_SG_List { + /* The following is a script fragment to move the buffer onto the + * bus and then link the next fragment or return */ + #define SCRIPT_MOVE_DATA_IN 0x09000000 + #define SCRIPT_MOVE_DATA_OUT 0x08000000 + __u32 ins; + __u32 pAddr; + #define SCRIPT_NOP 0x80000000 + #define SCRIPT_RETURN 0x90080000 +}; + +/* We use device->hostdata to store negotiated parameters. This is + * supposed to be a pointer to a device private area, but we cannot + * really use it as such since it will never be freed, so just use the + * 32 bits to cram the information. The SYNC negotiation sequence looks + * like: + * + * If DEV_NEGOTIATED_SYNC not set, tack and SDTR message on to the + * initial identify for the device and set DEV_BEGIN_SYNC_NEGOTATION + * If we get an SDTR reply, work out the SXFER parameters, squirrel + * them away here, clear DEV_BEGIN_SYNC_NEGOTIATION and set + * DEV_NEGOTIATED_SYNC. If we get a REJECT msg, squirrel + * + * + * 0:7 SXFER_REG negotiated value for this device + * 8:15 Current queue depth + * 16 negotiated SYNC flag + * 17 begin SYNC negotiation flag + * 18 device supports tag queueing */ +#define NCR_700_DEV_NEGOTIATED_SYNC (1<<16) +#define NCR_700_DEV_BEGIN_SYNC_NEGOTIATION (1<<17) +#define NCR_700_DEV_BEGIN_TAG_QUEUEING (1<<18) + +static inline void +NCR_700_set_SXFER(Scsi_Device *SDp, __u8 sxfer) +{ + ((__u32)SDp->hostdata) &= 0xffffff00; + ((__u32)SDp->hostdata) |= sxfer & 0xff; +} +static inline __u8 NCR_700_get_SXFER(Scsi_Device *SDp) +{ + return (((__u32)SDp->hostdata) & 0xff); +} +static inline void +NCR_700_set_depth(Scsi_Device *SDp, __u8 depth) +{ + ((__u32)SDp->hostdata) &= 0xffff00ff; + ((__u32)SDp->hostdata) |= (0xff00 & (depth << 8)); +} +static inline __u8 +NCR_700_get_depth(Scsi_Device *SDp) +{ + return ((((__u32)SDp->hostdata) & 0xff00)>>8); +} +static inline int +NCR_700_is_flag_set(Scsi_Device *SDp, __u32 flag) +{ + return (((__u32)SDp->hostdata) & flag) == flag; +} +static inline int +NCR_700_is_flag_clear(Scsi_Device *SDp, __u32 flag) +{ + return (((__u32)SDp->hostdata) & flag) == 0; +} +static inline void +NCR_700_set_flag(Scsi_Device *SDp, __u32 flag) +{ + ((__u32)SDp->hostdata) |= (flag & 0xffff0000); +} +static inline void +NCR_700_clear_flag(Scsi_Device *SDp, __u32 flag) +{ + ((__u32)SDp->hostdata) &= ~(flag & 0xffff0000); +} + +/* These represent the Nexus hashing functions. A Nexus in SCSI terms + * just means the identification of an outstanding command, by ITL + * (Initiator Target Lun) or ITLQ (Initiator Target Lun Tag). I'm not + * very keen on XOR based hashes, so these are based on number theory + * instead. All you need to do is to fix your hash bucket size and + * then choose reasonable strides which are coprime with the chosen + * bucket size + * + * Note: this mathematical hash can be made very efficient, if the + * compiler is good at optimising: Choose the number of buckets to be + * 2^n and the modulo becomes a logical and with (2^n-1). + * Additionally, if you chose the coprimes of the form 2^n-2^n the + * multiplication can be done by a shift and an addition. */ +#define MAX_ITL_HASH_BUCKETS 16 +#define ITL_HASH_PRIME 7 + +#define MAX_ITLQ_HASH_BUCKETS 64 +#define ITLQ_PUN_PRIME 7 +#define ITLQ_LUN_PRIME 3 + +static inline int +hash_ITL(__u8 pun, __u8 lun) +{ + return (pun*ITL_HASH_PRIME + lun) % MAX_ITL_HASH_BUCKETS; +} + +static inline int +hash_ITLQ(__u8 pun, __u8 lun, __u8 tag) +{ + return (pun*ITLQ_PUN_PRIME + lun*ITLQ_LUN_PRIME + tag) % MAX_ITLQ_HASH_BUCKETS; +} + +struct NCR_700_command_slot { + #define NCR_700_SLOT_MASK 0xFC + #define NCR_700_SLOT_MAGIC 0xb8 + #define NCR_700_SLOT_FREE (0|NCR_700_SLOT_MAGIC) /* slot may be used */ + #define NCR_700_SLOT_BUSY (1|NCR_700_SLOT_MAGIC) /* slot has command active on HA */ + #define NCR_700_SLOT_QUEUED (2|NCR_700_SLOT_MAGIC) /* slot has command to be made active on HA */ + __u8 state; + #define NCR_700_NO_TAG 0xdead + __u16 tag; + struct NCR_700_SG_List SG[NCR_700_SG_SEGMENTS+1]; + __u32 resume_offset; + Scsi_Cmnd *cmnd; + __u32 temp; + /* Doubly linked ITL/ITLQ list kept in strict time order + * (latest at the back) */ + struct NCR_700_command_slot *ITL_forw; + struct NCR_700_command_slot *ITL_back; + struct NCR_700_command_slot *ITLQ_forw; + struct NCR_700_command_slot *ITLQ_back; +}; + +struct NCR_700_Host_Parameters { + /* These must be filled in by the calling driver */ + int clock; /* board clock speed in MHz */ + __u32 base; /* the base for the port (copied to host) */ + __u8 differential:1; /* if we are differential */ +#ifdef __hppa__ + /* This option is for HP only. Set it if your chip is wired for + * little endian on this platform (which is big endian) */ + __u8 force_le_on_be:1; +#endif + + /* NOTHING BELOW HERE NEEDS ALTERING */ + __u8 fast:1; /* if we can alter the SCSI bus clock + speed (so can negiotiate sync) */ + + int sync_clock; /* The speed of the SYNC core */ + + __u32 *script; /* pointer to script location */ + __u32 pScript; /* physical mem addr of script */ + + /* This will be the host lock. Unfortunately, we can't use it + * at the moment because of the necessity of holding the + * io_request_lock */ + spinlock_t lock; + enum NCR_700_Host_State state; /* protected by state lock */ + Scsi_Cmnd *cmd; + + __u8 msgout[8]; + __u8 tag_negotiated; + __u8 status; + __u8 msgin[8]; + struct NCR_700_command_slot *slots; + int saved_slot_position; + int command_slot_count; /* protected by state lock */ + __u8 rev; + __u8 reselection_id; + /* flags for the host */ + + /* ITL list. ALL outstanding commands are hashed here in strict + * order, latest at the back */ + struct NCR_700_command_slot *ITL_Hash_forw[MAX_ITL_HASH_BUCKETS]; + struct NCR_700_command_slot *ITL_Hash_back[MAX_ITL_HASH_BUCKETS]; + + /* Only tagged outstanding commands are hashed here (also latest + * at the back) */ + struct NCR_700_command_slot *ITLQ_Hash_forw[MAX_ITLQ_HASH_BUCKETS]; + struct NCR_700_command_slot *ITLQ_Hash_back[MAX_ITLQ_HASH_BUCKETS]; + + /* Free list, singly linked by ITL_forw elements */ + struct NCR_700_command_slot *free_list; +}; + +/* + * 53C700 Register Interface - the offset from the Selected base + * I/O address */ +#ifdef __hppa__ +#define bE (hostdata->force_le_on_be ? 0 : 3) +#define bSWAP (hostdata->force_le_on_be) +#elif defined(__BIG_ENDIAN) +#define bE 3 +#define bSWAP 0 +#elif defined(__LITTLE_ENDIAN) +#define bE 0 +#define bSWAP 0 +#else +#error "__BIG_ENDIAN or __LITTLE_ENDIAN must be defined, did you include byteorder.h?" +#endif +#define bS_to_cpu(x) (bSWAP ? le32_to_cpu(x) : (x)) +#define bS_to_host(x) (bSWAP ? cpu_to_le32(x) : (x)) + +/* NOTE: These registers are in the LE register space only, the required byte + * swapping is done by the NCR_700_{read|write}[b] functions */ +#define SCNTL0_REG 0x00 +#define FULL_ARBITRATION 0xc0 +#define PARITY 0x08 +#define ENABLE_PARITY 0x04 +#define AUTO_ATN 0x02 +#define SCNTL1_REG 0x01 +#define SLOW_BUS 0x80 +#define ENABLE_SELECT 0x20 +#define ASSERT_RST 0x08 +#define ASSERT_EVEN_PARITY 0x04 +#define SDID_REG 0x02 +#define SIEN_REG 0x03 +#define PHASE_MM_INT 0x80 +#define FUNC_COMP_INT 0x40 +#define SEL_TIMEOUT_INT 0x20 +#define SELECT_INT 0x10 +#define GROSS_ERR_INT 0x08 +#define UX_DISC_INT 0x04 +#define RST_INT 0x02 +#define PAR_ERR_INT 0x01 +#define SCID_REG 0x04 +#define SXFER_REG 0x05 +#define ASYNC_OPERATION 0x00 +#define SODL_REG 0x06 +#define SOCL_REG 0x07 +#define SFBR_REG 0x08 +#define SIDL_REG 0x09 +#define SBDL_REG 0x0A +#define SBCL_REG 0x0B +/* read bits */ +#define SBCL_IO 0x01 +/*write bits */ +#define SYNC_DIV_AS_ASYNC 0x00 +#define SYNC_DIV_1_0 0x01 +#define SYNC_DIV_1_5 0x02 +#define SYNC_DIV_2_0 0x03 +#define DSTAT_REG 0x0C +#define ILGL_INST_DETECTED 0x01 +#define WATCH_DOG_INTERRUPT 0x02 +#define SCRIPT_INT_RECEIVED 0x04 +#define ABORTED 0x10 +#define SSTAT0_REG 0x0D +#define PARITY_ERROR 0x01 +#define SCSI_RESET_DETECTED 0x02 +#define UNEXPECTED_DISCONNECT 0x04 +#define SCSI_GROSS_ERROR 0x08 +#define SELECTED 0x10 +#define SELECTION_TIMEOUT 0x20 +#define FUNCTION_COMPLETE 0x40 +#define PHASE_MISMATCH 0x80 +#define SSTAT1_REG 0x0E +#define SIDL_REG_FULL 0x80 +#define SODR_REG_FULL 0x40 +#define SODL_REG_FULL 0x20 +#define SSTAT2_REG 0x0F +#define CTEST0_REG 0x14 +#define CTEST1_REG 0x15 +#define CTEST2_REG 0x16 +#define CTEST3_REG 0x17 +#define CTEST4_REG 0x18 +#define DISABLE_FIFO 0x00 +#define SLBE 0x10 +#define SFWR 0x08 +#define BYTE_LANE0 0x04 +#define BYTE_LANE1 0x05 +#define BYTE_LANE2 0x06 +#define BYTE_LANE3 0x07 +#define SCSI_ZMODE 0x20 +#define ZMODE 0x40 +#define CTEST5_REG 0x19 +#define MASTER_CONTROL 0x10 +#define DMA_DIRECTION 0x08 +#define CTEST7_REG 0x1B +#define DFP 0x08 +#define EVP 0x04 +#define DIFF 0x01 +#define CTEST6_REG 0x1A +#define TEMP_REG 0x1C +#define DFIFO_REG 0x20 +#define FLUSH_DMA_FIFO 0x80 +#define CLR_FIFO 0x40 +#define ISTAT_REG 0x21 +#define ABORT_OPERATION 0x80 +#define DMA_INT_PENDING 0x01 +#define SCSI_INT_PENDING 0x02 +#define CONNECTED 0x08 +#define CTEST8_REG 0x22 +#define LAST_DIS_ENBL 0x01 +#define SHORTEN_FILTERING 0x04 +#define ENABLE_ACTIVE_NEGATION 0x10 +#define GENERATE_RECEIVE_PARITY 0x20 +#define CTEST9_REG 0x23 +#define DBC_REG 0x24 +#define DCMD_REG 0x27 +#define DNAD_REG 0x28 +#define DIEN_REG 0x39 +#define ABORT_INT 0x10 +#define INT_INST_INT 0x04 +#define WD_INT 0x02 +#define ILGL_INST_INT 0x01 +#define DCNTL_REG 0x3B +#define SOFTWARE_RESET 0x01 +#define SCRPTS_16BITS 0x20 +#define ASYNC_DIV_2_0 0x00 +#define ASYNC_DIV_1_5 0x01 +#define ASYNC_DIV_1_0 0x02 +#define ASYNC_DIV_3_0 0x03 +#define DMODE_REG 0x34 +#define BURST_LENGTH_1 0x00 +#define BURST_LENGTH_2 0x40 +#define BURST_LENGTH_4 0x80 +#define BURST_LENGTH_8 0xC0 +#define BW16 32 +#define MODE_286 16 +#define IO_XFER 8 +#define FIXED_ADDR 4 + +#define DSP_REG 0x2C +#define DSPS_REG 0x30 + +/* Parameters to begin SDTR negotiations. Empirically, I find that + * the 53c700-66 cannot handle an offset >8, so don't change this */ +#define NCR_700_MAX_OFFSET 8 +#define NCR_700_MIN_XFERP 1 +#define NCR_700_MIN_PERIOD 25 /* for SDTR message, 100ns */ + +#define script_patch_32(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + __u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]) + value; \ + (script)[A_##symbol##_used[i]] = bS_to_host(val); \ + dma_cache_wback((unsigned long)&(script)[A_##symbol##_used[i]], 4); \ + DEBUG((" script, patching %s at %d to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], (value))); \ + } \ +} + +#define script_patch_32_abs(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + (script)[A_##symbol##_used[i]] = bS_to_host(value); \ + dma_cache_wback((unsigned long)&(script)[A_##symbol##_used[i]], 4); \ + DEBUG((" script, patching %s at %d to 0x%lx\n", \ + #symbol, A_##symbol##_used[i], (value))); \ + } \ +} + +/* Used for patching the SCSI ID in the SELECT instruction */ +#define script_patch_ID(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + __u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]); \ + val &= 0xff00ffff; \ + val |= ((value) & 0xff) << 16; \ + (script)[A_##symbol##_used[i]] = bS_to_host(val); \ + dma_cache_wback((unsigned long)&(script)[A_##symbol##_used[i]], 4); \ + DEBUG((" script, patching ID field %s at %d to 0x%x\n", \ + #symbol, A_##symbol##_used[i], val)); \ + } \ +} + +#define script_patch_16(script, symbol, value) \ +{ \ + int i; \ + for(i=0; i< (sizeof(A_##symbol##_used) / sizeof(__u32)); i++) { \ + __u32 val = bS_to_cpu((script)[A_##symbol##_used[i]]); \ + val &= 0xffff0000; \ + val |= ((value) & 0xffff); \ + (script)[A_##symbol##_used[i]] = bS_to_host(val); \ + dma_cache_wback((unsigned long)&(script)[A_##symbol##_used[i]], 4); \ + DEBUG((" script, patching ID field %s at %d to 0x%x\n", \ + #symbol, A_##symbol##_used[i], val)); \ + } \ +} + +#endif + +#ifdef MEM_MAPPED +static inline __u8 +NCR_700_readb(struct Scsi_Host *host, __u32 reg) +{ + const struct NCR_700_Host_Parameters *hostdata __attribute__((unused)) + = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + return readb(host->base + (reg^bE)); +} + +static inline __u32 +NCR_700_readl(struct Scsi_Host *host, __u32 reg) +{ + __u32 value = readl(host->base + reg); + const struct NCR_700_Host_Parameters *hostdata __attribute__((unused)) + = (struct NCR_700_Host_Parameters *)host->hostdata[0]; +#if 1 + /* sanity check the register */ + if((reg & 0x3) != 0) + BUG(); +#endif + + return bS_to_cpu(value); +} + +static inline void +NCR_700_writeb(__u8 value, struct Scsi_Host *host, __u32 reg) +{ + const struct NCR_700_Host_Parameters *hostdata __attribute__((unused)) + = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + writeb(value, host->base + (reg^bE)); +} + +static inline void +NCR_700_writel(__u32 value, struct Scsi_Host *host, __u32 reg) +{ + const struct NCR_700_Host_Parameters *hostdata __attribute__((unused)) + = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + +#if 1 + /* sanity check the register */ + if((reg & 0x3) != 0) + BUG(); +#endif + + writel(bS_to_host(value), host->base + reg); +} +#elif defined(IO_MAPPED) +static inline __u8 +NCR_700_readb(struct Scsi_Host *host, __u32 reg) +{ + const struct NCR_700_Host_Parameters *hostdata __attribute__((unused)) + = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + return inb(host->base + (reg^bE)); +} + +static inline __u32 +NCR_700_readl(struct Scsi_Host *host, __u32 reg) +{ + __u32 value = inl(host->base + reg); + const struct NCR_700_Host_Parameters *hostdata __attribute__((unused)) + = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + +#if 1 + /* sanity check the register */ + if((reg & 0x3) != 0) + BUG(); +#endif + + return bS_to_cpu(value); +} + +static inline void +NCR_700_writeb(__u8 value, struct Scsi_Host *host, __u32 reg) +{ + const struct NCR_700_Host_Parameters *hostdata __attribute__((unused)) + = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + + outb(value, host->base + (reg^bE)); +} + +static inline void +NCR_700_writel(__u32 value, struct Scsi_Host *host, __u32 reg) +{ + const struct NCR_700_Host_Parameters *hostdata __attribute__((unused)) + = (struct NCR_700_Host_Parameters *)host->hostdata[0]; + +#if 1 + /* sanity check the register */ + if((reg & 0x3) != 0) + BUG(); +#endif + + outl(bS_to_host(value), host->base + reg); +} +#endif diff --git a/drivers/scsi/53c700.scr b/drivers/scsi/53c700.scr new file mode 100644 index 000000000000..30d22a34dbea --- /dev/null +++ b/drivers/scsi/53c700.scr @@ -0,0 +1,404 @@ +; Script for the NCR (or symbios) 53c700 and 53c700-66 chip +; +; Copyright (C) 2001 James.Bottomley@HansenPartnership.com +;;----------------------------------------------------------------------------- +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;; +;;----------------------------------------------------------------------------- +; +; This script is designed to be modified for the particular command in +; operation. The particular variables pertaining to the commands are: +; +ABSOLUTE Device_ID = 0 ; ID of target for command +ABSOLUTE MessageCount = 0 ; Number of bytes in message +ABSOLUTE MessageLocation = 0 ; Addr of message +ABSOLUTE CommandCount = 0 ; Number of bytes in command +ABSOLUTE CommandAddress = 0 ; Addr of Command +ABSOLUTE StatusAddress = 0 ; Addr to receive status return +ABSOLUTE ReceiveMsgAddress = 0 ; Addr to receive msg +; +; This is the magic component for handling scatter-gather. Each of the +; SG components is preceeded by a script fragment which moves the +; necessary amount of data and jumps to the next SG segment. The final +; SG segment jumps back to . However, this address is the first SG script +; segment. +; +ABSOLUTE SGScriptStartAddress = 0 + +; The following represent status interrupts we use 3 hex digits for +; this: 0xPRS where + +; P: +ABSOLUTE AFTER_SELECTION = 0x100 +ABSOLUTE BEFORE_CMD = 0x200 +ABSOLUTE AFTER_CMD = 0x300 +ABSOLUTE AFTER_STATUS = 0x400 +ABSOLUTE AFTER_DATA_IN = 0x500 +ABSOLUTE AFTER_DATA_OUT = 0x600 +ABSOLUTE DURING_DATA_IN = 0x700 + +; R: +ABSOLUTE NOT_MSG_OUT = 0x10 +ABSOLUTE UNEXPECTED_PHASE = 0x20 +ABSOLUTE NOT_MSG_IN = 0x30 +ABSOLUTE UNEXPECTED_MSG = 0x40 +ABSOLUTE MSG_IN = 0x50 +ABSOLUTE SDTR_MSG_R = 0x60 +ABSOLUTE REJECT_MSG_R = 0x70 +ABSOLUTE DISCONNECT = 0x80 +ABSOLUTE MSG_OUT = 0x90 +ABSOLUTE WDTR_MSG_R = 0xA0 + +; S: +ABSOLUTE GOOD_STATUS = 0x1 + +; Combinations, since the script assembler can't process | +ABSOLUTE NOT_MSG_OUT_AFTER_SELECTION = 0x110 +ABSOLUTE UNEXPECTED_PHASE_BEFORE_CMD = 0x220 +ABSOLUTE UNEXPECTED_PHASE_AFTER_CMD = 0x320 +ABSOLUTE NOT_MSG_IN_AFTER_STATUS = 0x430 +ABSOLUTE GOOD_STATUS_AFTER_STATUS = 0x401 +ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_IN = 0x520 +ABSOLUTE UNEXPECTED_PHASE_AFTER_DATA_OUT = 0x620 +ABSOLUTE UNEXPECTED_MSG_BEFORE_CMD = 0x240 +ABSOLUTE MSG_IN_BEFORE_CMD = 0x250 +ABSOLUTE MSG_IN_AFTER_CMD = 0x350 +ABSOLUTE SDTR_MSG_BEFORE_CMD = 0x260 +ABSOLUTE REJECT_MSG_BEFORE_CMD = 0x270 +ABSOLUTE DISCONNECT_AFTER_CMD = 0x380 +ABSOLUTE SDTR_MSG_AFTER_CMD = 0x360 +ABSOLUTE WDTR_MSG_AFTER_CMD = 0x3A0 +ABSOLUTE DISCONNECT_AFTER_DATA = 0x580 +ABSOLUTE MSG_IN_AFTER_DATA_IN = 0x550 +ABSOLUTE MSG_IN_AFTER_DATA_OUT = 0x650 +ABSOLUTE MSG_OUT_AFTER_DATA_IN = 0x590 +ABSOLUTE DATA_IN_AFTER_DATA_IN = 0x5a0 +ABSOLUTE MSG_IN_DURING_DATA_IN = 0x750 +ABSOLUTE DISCONNECT_DURING_DATA = 0x780 + +; +; Other interrupt conditions +; +ABSOLUTE RESELECTED_DURING_SELECTION = 0x1000 +ABSOLUTE COMPLETED_SELECTION_AS_TARGET = 0x1001 +ABSOLUTE RESELECTION_IDENTIFIED = 0x1003 +; +; Fatal interrupt conditions. If you add to this, also add to the +; array of corresponding messages +; +ABSOLUTE FATAL = 0x2000 +ABSOLUTE FATAL_UNEXPECTED_RESELECTION_MSG = 0x2000 +ABSOLUTE FATAL_SEND_MSG = 0x2001 +ABSOLUTE FATAL_NOT_MSG_IN_AFTER_SELECTION = 0x2002 +ABSOLUTE FATAL_ILLEGAL_MSG_LENGTH = 0x2003 + +ABSOLUTE DEBUG_INTERRUPT = 0x3000 +ABSOLUTE DEBUG_INTERRUPT1 = 0x3001 +ABSOLUTE DEBUG_INTERRUPT2 = 0x3002 +ABSOLUTE DEBUG_INTERRUPT3 = 0x3003 +ABSOLUTE DEBUG_INTERRUPT4 = 0x3004 +ABSOLUTE DEBUG_INTERRUPT5 = 0x3005 +ABSOLUTE DEBUG_INTERRUPT6 = 0x3006 + + +; +; SCSI Messages we interpret in the script +; +ABSOLUTE EXTENDED_MSG = 0x01 +ABSOLUTE SDTR_MSG = 0x01 +ABSOLUTE SAVE_DATA_PTRS_MSG = 0x02 +ABSOLUTE RESTORE_DATA_PTRS_MSG = 0x03 +ABSOLUTE WDTR_MSG = 0x03 +ABSOLUTE DISCONNECT_MSG = 0x04 +ABSOLUTE REJECT_MSG = 0x07 +ABSOLUTE PARITY_ERROR_MSG = 0x09 +ABSOLUTE SIMPLE_TAG_MSG = 0x20 +ABSOLUTE IDENTIFY_MSG = 0x80 +ABSOLUTE IDENTIFY_MSG_MASK = 0x7F +ABSOLUTE TWO_BYTE_MSG = 0x20 +ABSOLUTE TWO_BYTE_MSG_MASK = 0x0F + +; This is where the script begins + +ENTRY StartUp + +StartUp: + SELECT ATN Device_ID, Reselect + JUMP Finish, WHEN STATUS + JUMP SendIdentifyMsg, IF MSG_OUT + INT NOT_MSG_OUT_AFTER_SELECTION + +Reselect: + WAIT RESELECT SelectedAsTarget + INT RESELECTED_DURING_SELECTION, WHEN MSG_IN + INT FATAL_NOT_MSG_IN_AFTER_SELECTION + + ENTRY GetReselectionData +GetReselectionData: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + INT RESELECTION_IDENTIFIED + + ENTRY GetReselectionWithTag +GetReselectionWithTag: + MOVE 3, ReceiveMsgAddress, WHEN MSG_IN + INT RESELECTION_IDENTIFIED + + ENTRY SelectedAsTarget +SelectedAsTarget: +; Basically tell the selecting device that there's nothing here + SET TARGET + DISCONNECT + CLEAR TARGET + INT COMPLETED_SELECTION_AS_TARGET +; +; These are the messaging entries +; +; Send a message. Message count should be correctly patched + ENTRY SendMessage +SendMessage: + MOVE MessageCount, MessageLocation, WHEN MSG_OUT +ResumeSendMessage: + RETURN, WHEN NOT MSG_OUT + INT FATAL_SEND_MSG + + ENTRY SendMessagePhaseMismatch +SendMessagePhaseMismatch: + CLEAR ACK + JUMP ResumeSendMessage +; +; Receive a message. Need to identify the message to +; receive it correctly + ENTRY ReceiveMessage +ReceiveMessage: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN +; +; Use this entry if we've just tried to look at the first byte +; of the message and want to process it further +ProcessReceiveMessage: + JUMP ReceiveExtendedMessage, IF EXTENDED_MSG + RETURN, IF NOT TWO_BYTE_MSG, AND MASK TWO_BYTE_MSG_MASK + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN + RETURN +ReceiveExtendedMessage: + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 1, WHEN MSG_IN + JUMP Receive1Byte, IF 0x01 + JUMP Receive2Byte, IF 0x02 + JUMP Receive3Byte, IF 0x03 + JUMP Receive4Byte, IF 0x04 + JUMP Receive5Byte, IF 0x05 + INT FATAL_ILLEGAL_MSG_LENGTH +Receive1Byte: + CLEAR ACK + MOVE 1, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive2Byte: + CLEAR ACK + MOVE 2, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive3Byte: + CLEAR ACK + MOVE 3, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive4Byte: + CLEAR ACK + MOVE 4, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +Receive5Byte: + CLEAR ACK + MOVE 5, ReceiveMsgAddress + 2, WHEN MSG_IN + RETURN +; +; Come here from the message processor to ignore the message +; + ENTRY IgnoreMessage +IgnoreMessage: + CLEAR ACK + RETURN +; +; Come here to send a reply to a message +; + ENTRY SendMessageWithATN +SendMessageWithATN: + SET ATN + CLEAR ACK + JUMP SendMessage + +SendIdentifyMsg: + CALL SendMessage + JUMP SendCommand + +IgnoreMsgBeforeCommand: + CLEAR ACK + ENTRY SendCommand +SendCommand: + JUMP Finish, WHEN STATUS + JUMP MsgInBeforeCommand, IF MSG_IN + INT UNEXPECTED_PHASE_BEFORE_CMD, IF NOT CMD + MOVE CommandCount, CommandAddress, WHEN CMD +ResumeSendCommand: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterCmd, IF MSG_IN + JUMP DataIn, IF DATA_IN + JUMP DataOut, IF DATA_OUT + INT UNEXPECTED_PHASE_AFTER_CMD + +IgnoreMsgDuringData: + CLEAR ACK + ; fall through to MsgInDuringData + +Entry MsgInDuringData +MsgInDuringData: +; +; Could be we have nothing more to transfer +; + JUMP Finish, WHEN STATUS + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectDuringDataIn, IF DISCONNECT_MSG + JUMP IgnoreMsgDuringData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgDuringData, IF RESTORE_DATA_PTRS_MSG + INT MSG_IN_DURING_DATA_IN + +MsgInAfterCmd: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterCmd, IF DISCONNECT_MSG + JUMP IgnoreMsgInAfterCmd, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgInAfterCmd, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_CMD + CLEAR ACK + JUMP ResumeSendCommand + +IgnoreMsgInAfterCmd: + CLEAR ACK + JUMP ResumeSendCommand + +DisconnectAfterCmd: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect1 +Disconnect1: + INT DISCONNECT_AFTER_CMD + ENTRY Disconnect2 +Disconnect2: +; We return here after a reselection + CLEAR ACK + JUMP ResumeSendCommand + +MsgInBeforeCommand: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP IgnoreMsgBeforeCommand, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgBeforeCommand, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_BEFORE_CMD + CLEAR ACK + JUMP SendCommand + +DataIn: + CALL SGScriptStartAddress +ResumeDataIn: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterDataIn, IF MSG_IN + JUMP DataInAfterDataIn, if DATA_IN + INT MSG_OUT_AFTER_DATA_IN, if MSG_OUT + INT UNEXPECTED_PHASE_AFTER_DATA_IN + +DataInAfterDataIn: + INT DATA_IN_AFTER_DATA_IN + JUMP ResumeDataIn + +DataOut: + CALL SGScriptStartAddress +ResumeDataOut: + JUMP Finish, WHEN STATUS + JUMP MsgInAfterDataOut, IF MSG_IN + INT UNEXPECTED_PHASE_AFTER_DATA_OUT + +MsgInAfterDataIn: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterDataIn, IF DISCONNECT_MSG + JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_DATA_IN + CLEAR ACK + JUMP ResumeDataIn + +DisconnectDuringDataIn: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect3 +Disconnect3: + INT DISCONNECT_DURING_DATA + ENTRY Disconnect4 +Disconnect4: +; we return here after a reselection + CLEAR ACK + JUMP ResumeSendCommand + + +DisconnectAfterDataIn: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect5 +Disconnect5: + INT DISCONNECT_AFTER_DATA + ENTRY Disconnect6 +Disconnect6: +; we return here after a reselection + CLEAR ACK + JUMP ResumeDataIn + +MsgInAfterDataOut: + MOVE 1, ReceiveMsgAddress, WHEN MSG_IN + JUMP DisconnectAfterDataOut, if DISCONNECT_MSG + JUMP IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG + JUMP IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG + CALL ProcessReceiveMessage + INT MSG_IN_AFTER_DATA_OUT + CLEAR ACK + JUMP ResumeDataOut + +IgnoreMsgAfterData: + CLEAR ACK +; Data in and out do the same thing on resume, so pick one + JUMP ResumeDataIn + +DisconnectAfterDataOut: + CLEAR ACK + WAIT DISCONNECT + ENTRY Disconnect7 +Disconnect7: + INT DISCONNECT_AFTER_DATA + ENTRY Disconnect8 +Disconnect8: +; we return here after a reselection + CLEAR ACK + JUMP ResumeDataOut + +Finish: + MOVE 1, StatusAddress, WHEN STATUS + INT NOT_MSG_IN_AFTER_STATUS, WHEN NOT MSG_IN + CALL ReceiveMessage + CLEAR ACK + WAIT DISCONNECT + ENTRY Finish1 +Finish1: + INT GOOD_STATUS_AFTER_STATUS + ENTRY Finish2 +Finish2: + diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index d77f242d81ae..68560498e442 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -33,7 +33,7 @@ mainmenu_option next_comment comment 'SCSI low-level drivers' if [ "$CONFIG_SGI_IP22" = "y" ]; then - dep_tristate 'SGI WD93C93 SCSI Driver' CONFIG_SCSI_SGIWD93 $CONFIG_SCSI + dep_tristate 'SGI WD93C93 SCSI Driver' CONFIG_SGIWD93_SCSI $CONFIG_SCSI fi if [ "$CONFIG_DECSTATION" = "y" ]; then if [ "$CONFIG_TC" = "y" ]; then @@ -59,6 +59,7 @@ if [ "$CONFIG_SCSI_AIC7XXX" != "y" ]; then bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_OLD_PROC_STATS fi fi +dep_tristate 'Adaptec I2O RAID support ' CONFIG_SCSI_DPT_I2O $CONFIG_SCSI dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI $CONFIG_PCI @@ -85,7 +86,7 @@ dep_tristate 'Future Domain 16xx SCSI/AHA-2920A support' CONFIG_SCSI_FUTURE_DOMA if [ "$CONFIG_MCA" = "y" ]; then dep_tristate 'Future Domain MCS-600/700 SCSI support' CONFIG_SCSI_FD_MCS $CONFIG_SCSI fi -dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI +dep_tristate 'Intel/ICP (former GDT SCSI Disk Array) RAID Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then bool ' Enable NCR53c400 extensions' CONFIG_SCSI_GENERIC_NCR53C400 @@ -114,6 +115,10 @@ if [ "$CONFIG_PARPORT" != "n" ]; then fi fi dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI +dep_tristate 'NCR Dual 700 MCA SCSI support' CONFIG_SCSI_NCR_D700 $CONFIG_SCSI $CONFIG_MCA +if [ "$CONFIG_PARISC" = "y" ]; then + dep_tristate 'HP LASI SCSI support for 53c700' CONFIG_SCSI_LASI70 $CONFIG_SCSI +fi dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI $CONFIG_PCI if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then bool ' always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync @@ -151,6 +156,9 @@ dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI if [ "$CONFIG_PCI" = "y" ]; then dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI dep_tristate 'Qlogic ISP FC SCSI support' CONFIG_SCSI_QLOGIC_FC $CONFIG_SCSI + if [ "$CONFIG_SCSI_QLOGIC_FC" != "n" ]; then + bool ' Include loadable firmware in driver' CONFIG_SCSI_QLOGIC_FC_FIRMWARE + fi dep_tristate 'Qlogic QLA 1280 SCSI support' CONFIG_SCSI_QLOGIC_1280 $CONFIG_SCSI fi if [ "$CONFIG_X86" = "y" ]; then diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index fff2cf8d263d..7fe4fe985dc7 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -31,7 +31,7 @@ else endif endif -export-objs := scsi_syms.o +export-objs := scsi_syms.o 53c700.o 53c700-mem.o CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS @@ -47,6 +47,7 @@ obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o obj-$(CONFIG_GVP11_SCSI) += gvp11.o wd33c93.o obj-$(CONFIG_MVME147_SCSI) += mvme147.o wd33c93.o +obj-$(CONFIG_SGIWD93_SCSI) += sgiwd93.o wd33c93.o obj-$(CONFIG_CYBERSTORM_SCSI) += NCR53C9x.o cyberstorm.o obj-$(CONFIG_CYBERSTORMII_SCSI) += NCR53C9x.o cyberstormII.o obj-$(CONFIG_BLZ2060_SCSI) += NCR53C9x.o blz2060.o @@ -65,6 +66,7 @@ obj-$(CONFIG_SCSI_PCI2000) += pci2000.o obj-$(CONFIG_SCSI_PCI2220I) += pci2220i.o obj-$(CONFIG_SCSI_PSI240I) += psi240i.o obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o +obj-$(CONFIG_SCSI_DPT_I2O) += dpt_i2o.o obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o obj-$(CONFIG_SCSI_AHA152X) += aha152x.o @@ -80,6 +82,7 @@ obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o obj-$(CONFIG_SCSI_IN2000) += in2000.o obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o +obj-$(CONFIG_SCSI_NCR_D700) += NCR_D700.o 53c700.o obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o @@ -121,6 +124,8 @@ obj-$(CONFIG_JAZZ_ESP) += NCR53C9x.o jazz_esp.o obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o 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-mem.o ifeq ($(CONFIG_ARCH_ACORN),y) mod-subdirs += ../acorn/scsi @@ -193,3 +198,22 @@ sim710_d.h: sim710.scr script_asm.pl sim710_u.h: sim710_d.h sim710.o : sim710_d.h + +53c700_d.h: 53c700.scr script_asm.pl + $(PERL) -s script_asm.pl -ncr7x0_family < 53c700.scr + rm -f scriptu.h + mv script.h 53c700_d.h + +53c700.o: 53c700_d.h + +53c700-mem.o: 53c700_d.h + +53c700-mem.c: 53c700.c + echo "/* WARNING: GENERATED FILE (from $<), DO NOT MODIFY */" > $@ + echo "#define MEM_MAPPED" >> $@ + cat $< >> $@ + +cpqfc.o: cpqfcTSinit.o cpqfcTScontrol.o cpqfcTSi2c.o cpqfcTSworker.o \ + cpqfcTStrigger.o + $(LD) -r -o cpqfc.o cpqfcTSinit.o cpqfcTScontrol.o \ + cpqfcTSi2c.o cpqfcTSworker.o cpqfcTStrigger.o diff --git a/drivers/scsi/NCR_D700.c b/drivers/scsi/NCR_D700.c new file mode 100644 index 000000000000..80a6c1d52f1e --- /dev/null +++ b/drivers/scsi/NCR_D700.c @@ -0,0 +1,326 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR Dual 700 MCA SCSI Driver + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com +**----------------------------------------------------------------------------- +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +**----------------------------------------------------------------------------- + */ + +/* Notes: + * + * Most of the work is done in the chip specific module, 53c700.o + * + * TODO List: + * + * 1. Extract the SCSI ID from the voyager CMOS table (necessary to + * support multi-host environments. + * + * */ + + +/* CHANGELOG + * + * Version 2.1 + * + * Modularise the driver into a Board piece (this file) and a chip + * piece 53c700.[ch] and 53c700.scr, added module options. You can + * now specify the scsi id by the parameters + * + * NCR_D700=slot:<n> [siop:<n>] id:<n> .... + * + * They need to be comma separated if compiled into the kernel + * + * Version 2.0 + * + * Initial implementation of TCQ (Tag Command Queueing). TCQ is full + * featured and uses the clock algorithm to keep track of outstanding + * tags and guard against individual tag starvation. Also fixed a bug + * in all of the 1.x versions where the D700_data_residue() function + * was returning results off by 32 bytes (and thus causing the same 32 + * bytes to be written twice corrupting the data block). It turns out + * the 53c700 only has a 6 bit DBC and DFIFO registers not 7 bit ones + * like the 53c710 (The 710 is the only data manual still available, + * which I'd been using to program the 700). + * + * Version 1.2 + * + * Much improved message handling engine + * + * Version 1.1 + * + * Add code to handle selection reasonably correctly. By the time we + * get the selection interrupt, we've already responded, but drop off the + * bus and hope the selector will go away. + * + * Version 1.0: + * + * Initial release. Fully functional except for procfs and tag + * command queueing. Has only been tested on cards with 53c700-66 + * chips and only single ended. Features are + * + * 1. Synchronous data transfers to offset 8 (limit of 700-66) and + * 100ns (10MHz) limit of SCSI-2 + * + * 2. Disconnection and reselection + * + * Testing: + * + * I've only really tested this with the 700-66 chip, but have done + * soak tests in multi-device environments to verify that + * disconnections and reselections are being processed correctly. + * */ + +#define NCR_D700_VERSION "2.1" + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/mca.h> +#include <asm/dma.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/byteorder.h> +#include <linux/blk.h> +#include <linux/module.h> + + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#include "53c700.h" +#include "NCR_D700.h" + +#ifndef CONFIG_MCA +#error "NCR_D700 driver only compiles for MCA" +#endif + +#ifdef NCR_D700_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +char *NCR_D700; /* command line from insmod */ + +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("NCR Dual700 SCSI Driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(NCR_D700, "s"); + +static __u8 __initdata id_array[2*(MCA_MAX_SLOT_NR + 1)] = + { [0 ... 2*(MCA_MAX_SLOT_NR + 1)-1] = 7 }; + +#ifdef MODULE +#define ARG_SEP ' ' +#else +#define ARG_SEP ',' +#endif + +static int __init +param_setup(char *string) +{ + char *pos = string, *next; + int slot = -1, siop = -1; + + while(pos != NULL && (next = strchr(pos, ':')) != NULL) { + int val = (int)simple_strtoul(++next, NULL, 0); + + if(!strncmp(pos, "slot:", 5)) + slot = val; + else if(!strncmp(pos, "siop:", 5)) + siop = val; + else if(!strncmp(pos, "id:", 3)) { + if(slot == -1) { + printk(KERN_WARNING "NCR D700: Must specify slot for id parameter\n"); + } else if(slot > MCA_MAX_SLOT_NR) { + printk(KERN_WARNING "NCR D700: Illegal slot %d for id %d\n", slot, val); + } else { + if(siop != 0 && siop != 1) { + id_array[slot*2] = val; + id_array[slot*2 + 1] =val; + } else { + id_array[slot*2 + siop] = val; + } + } + } + if((pos = strchr(pos, ARG_SEP)) != NULL) + pos++; + } + return 1; +} + +#ifndef MODULE +__setup("NCR_D700=", param_setup); +#endif + +/* Detect a D700 card. Note, because of the set up---the chips are + * essentially connectecd to the MCA bus independently, it is easier + * to set them up as two separate host adapters, rather than one + * adapter with two channels */ +STATIC int __init +D700_detect(Scsi_Host_Template *tpnt) +{ + int slot = 0; + int found = 0; + int differential; + int banner = 1; + + if(!MCA_bus) + return 0; + +#ifdef MODULE + if(NCR_D700) + param_setup(NCR_D700); +#endif + + for(slot = 0; (slot = mca_find_adapter(NCR_D700_MCA_ID, slot)) != MCA_NOTFOUND; slot++) { + int irq, i; + int pos3j, pos3k, pos3a, pos3b, pos4; + __u32 base_addr, offset_addr; + struct Scsi_Host *host = NULL; + + /* enable board interrupt */ + pos4 = mca_read_pos(slot, 4); + pos4 |= 0x4; + mca_write_pos(slot, 4, pos4); + + mca_write_pos(slot, 6, 9); + pos3j = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 10); + pos3k = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 0); + pos3a = mca_read_pos(slot, 3); + mca_write_pos(slot, 6, 1); + pos3b = mca_read_pos(slot, 3); + + base_addr = ((pos3j << 8) | pos3k) & 0xfffffff0; + offset_addr = ((pos3a << 8) | pos3b) & 0xffffff70; + + irq = (pos4 & 0x3) + 11; + if(irq >= 13) + irq++; + if(banner) { + printk(KERN_NOTICE "NCR D700: Driver Version " NCR_D700_VERSION "\n" + "NCR D700: Copyright (c) 2001 by James.Bottomley@HansenPartnership.com\n" + "NCR D700:\n"); + banner = 0; + } + printk(KERN_NOTICE "NCR D700: found in slot %d irq = %d I/O base = 0x%x\n", slot, irq, offset_addr); + + tpnt->proc_name = "NCR_D700"; + + /*outb(BOARD_RESET, base_addr);*/ + + /* clear any pending interrupts */ + (void)inb(base_addr + 0x08); + /* get modctl, used later for setting diff bits */ + switch(differential = (inb(base_addr + 0x08) >> 6)) { + case 0x00: + /* only SIOP1 differential */ + differential = 0x02; + break; + case 0x01: + /* Both SIOPs differential */ + differential = 0x03; + break; + case 0x03: + /* No SIOPs differential */ + differential = 0x00; + break; + default: + printk(KERN_ERR "D700: UNEXPECTED DIFFERENTIAL RESULT 0x%02x\n", + differential); + differential = 0x00; + break; + } + + /* plumb in both 700 chips */ + for(i=0; i<2; i++) { + __u32 region = offset_addr | (0x80 * i); + struct NCR_700_Host_Parameters *hostdata = + kmalloc(sizeof(struct NCR_700_Host_Parameters), + GFP_KERNEL); + if(hostdata == NULL) { + printk(KERN_ERR "NCR D700: Failed to allocate host data for channel %d, detatching\n", i); + continue; + } + memset(hostdata, 0, sizeof(struct NCR_700_Host_Parameters)); + if(request_region(region, 64, "NCR_D700") == NULL) { + printk(KERN_ERR "NCR D700: Failed to reserve IO region 0x%x\n", region); + kfree(hostdata); + continue; + } + + /* Fill in the three required pieces of hostdata */ + hostdata->base = region; + hostdata->differential = (((1<<i) & differential) != 0); + hostdata->clock = NCR_D700_CLOCK_MHZ; + /* and register the chip */ + if((host = NCR_700_detect(tpnt, hostdata)) == NULL) { + kfree(hostdata); + release_region(host->base, 64); + continue; + } + host->irq = irq; + /* FIXME: Read this from SUS */ + host->this_id = id_array[slot * 2 + i]; + printk(KERN_NOTICE "NCR D700: SIOP%d, SCSI id is %d\n", + i, host->this_id); + if(request_irq(irq, NCR_700_intr, SA_SHIRQ, "NCR_D700", host)) { + printk(KERN_ERR "NCR D700, channel %d: irq problem, detatching\n", i); + scsi_unregister(host); + NCR_700_release(host); + continue; + } + found++; + } + } + + return found; +} + + +STATIC int +D700_release(struct Scsi_Host *host) +{ + struct D700_Host_Parameters *hostdata = + (struct D700_Host_Parameters *)host->hostdata[0]; + + NCR_700_release(host); + kfree(hostdata); + free_irq(host->irq, host); + release_region(host->base, 64); + return 1; +} + + +static Scsi_Host_Template driver_template = NCR_D700_SCSI; + +#include "scsi_module.c" + diff --git a/drivers/scsi/NCR_D700.h b/drivers/scsi/NCR_D700.h new file mode 100644 index 000000000000..cd9e4829b3a9 --- /dev/null +++ b/drivers/scsi/NCR_D700.h @@ -0,0 +1,45 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* NCR Dual 700 MCA SCSI Driver + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com + */ + +#ifndef _NCR_D700_H +#define _NCR_D700_H + +/* Don't turn on debugging messages */ +#undef NCR_D700_DEBUG + +/* The MCA identifier */ +#define NCR_D700_MCA_ID 0x0092 + +static int D700_detect(Scsi_Host_Template *); +static int D700_release(struct Scsi_Host *host); + + +/* Host template. Note the name and proc_name are optional, all the + * remaining parameters shown below must be filled in. The 53c700 + * routine NCR_700_detect will fill in all of the missing routines */ +#define NCR_D700_SCSI { \ + name: "NCR Dual 700 MCA", \ + proc_name: "NCR_D700", \ + detect: D700_detect, \ + release: D700_release, \ + this_id: 7, \ +} + + +/* Defines for the Board registers */ +#define BOARD_RESET 0x80 /* board level reset */ +#define ADD_PARENB 0x04 /* Address Parity Enabled */ +#define DAT_PARENB 0x01 /* Data Parity Enabled */ +#define SFBK_ENB 0x10 /* SFDBK Interrupt Enabled */ +#define LED0GREEN 0x20 /* Led 0 (red 0; green 1) */ +#define LED1GREEN 0x40 /* Led 1 (red 0; green 1) */ +#define LED0RED 0xDF /* Led 0 (red 0; green 1) */ +#define LED1RED 0xBF /* Led 1 (red 0; green 1) */ + +#define NCR_D700_CLOCK_MHZ 50 + +#endif diff --git a/drivers/scsi/README.53c700 b/drivers/scsi/README.53c700 new file mode 100644 index 000000000000..20f895946cd7 --- /dev/null +++ b/drivers/scsi/README.53c700 @@ -0,0 +1,17 @@ +This driver supports the 53c700 and 53c700-66 chips only. It is full +featured and does sync (-66 only), disconnects and tag command +queueing. + +Since the 53c700 must be interfaced to a bus, you need to wrapper the +card detector around this driver. For an example, see the +NCR_D700.[ch] files. + +The comments in the 53c700.[ch] files tell you which parts you need to +fill in to get the driver working. + +The driver is currently I/O mapped only, but it should be easy enough +to memory map (just make the port reads #defines with MEM_MAPPED for +memory mapping or nothing for I/O mapping, specify an extra rule for +53c700-mem.o with the -DMEM_MAPPED flag and make your driver use it, +that way the make rules will generate the correct version). + diff --git a/drivers/scsi/README.dpti b/drivers/scsi/README.dpti new file mode 100644 index 000000000000..daaab2c057f9 --- /dev/null +++ b/drivers/scsi/README.dpti @@ -0,0 +1,83 @@ + /* TERMS AND CONDITIONS OF USE + * + * Redistribution and use in source form, with or without modification, are + * permitted provided that redistributions of source code must retain the + * above copyright notice, this list of conditions and the following disclaimer. + * + * This software is provided `as is' by Adaptec and + * any express or implied warranties, including, but not limited to, the + * implied warranties of merchantability and fitness for a particular purpose, + * are disclaimed. In no event shall Adaptec be + * liable for any direct, indirect, incidental, special, exemplary or + * consequential damages (including, but not limited to, procurement of + * substitute goods or services; loss of use, data, or profits; or business + * interruptions) however caused and on any theory of liability, whether in + * contract, strict liability, or tort (including negligence or otherwise) + * arising in any way out of the use of this driver software, even if advised + * of the possibility of such damage. + * + **************************************************************** + * This driver supports the Adaptec I2O RAID and DPT SmartRAID V I2O boards. + * + * CREDITS: + * The original linux driver was ported to Linux by Karen White while at + * Dell Computer. It was ported from Bob Pasteur's (of DPT) original + * non-Linux driver. Mark Salyzyn and Bob Pasteur consulted on the original + * driver. + * + * 2.0 version of the driver by Deanna Bonds and Mark Salyzyn. + * + * HISTORY: + * The driver was originally ported to linux version 2.0.34 + * + * V2.0 Rewrite of driver. Re-architectured based on i2o subsystem. + * This was the first full GPL version since the last version used + * i2osig headers which were not GPL. Developer Testing version. + * V2.1 Internal testing + * V2.2 First released version + * + * V2.3 + * Changes: + * Added Raptor Support + * Fixed bug causing system to hang under extreme load with + * management utilities running (removed GFP_DMA from kmalloc flags) + * + * + * V2.4 First version ready to be submitted to be embedded in the kernel + * Changes: + * Implemented suggestions from Alan Cox + * Added calculation of resid for sg layer + * Better error handling + * Added checking underflow condtions + * Added DATAPROTECT checking + * Changed error return codes + * Fixed pointer bug in bus reset routine + * Enabled hba reset from ioctls (allows a FW flash to reboot and use the new + * FW without having to reboot) + * Changed proc output + * + * TODO: + * Add 64 bit Scatter Gather when compiled on 64 bit architectures + * Add sparse lun scanning + * Add code that checks if a device that had been taken offline is + * now online (at the FW level) when test unit ready or inquiry + * command from scsi-core + * Add proc read interface + * busrescan command + * rescan command + * Add code to rescan routine that notifies scsi-core about new devices + * Add support for C-PCI (hotplug stuff) + * Add ioctl passthru error recovery + * + * NOTES: + * The DPT card optimizes the order of processing commands. Consequently, + * a command may take up to 6 minutes to complete after it has been sent + * to the board. + * + * The files dpti_ioctl.h dptsig.h osd_defs.h osd_util.h sys_info.h are part of the + * interface files for Adaptecs managment routines. These define the structures used + * in the ioctls. They are written to be portable. They are hard to read, but I need + * to use them 'as is' or I can miss changes in the interface. + * + */ + diff --git a/drivers/scsi/dec_esp.c b/drivers/scsi/dec_esp.c index 9a51418d7442..1106dab4ff2d 100644 --- a/drivers/scsi/dec_esp.c +++ b/drivers/scsi/dec_esp.c @@ -9,6 +9,14 @@ * Copyright (C) 1997 Thomas Bogendoerfer (tsbogend@alpha.franken.de) * * jazz_esp is based on David S. Miller's ESP driver and cyber_esp + * + * 20000819 - Small PMAZ-AA fixes by Florian Lohoff <flo@rfc822.org> + * Be warned the PMAZ-AA works currently as a single card. + * Dont try to put multiple cards in one machine - They are + * both detected but it may crash under high load garbling your + * data. + * 20001005 - Initialization fixes for 2.4.0-test9 + * Florian Lohoff <flo@rfc822.org> */ #include <linux/kernel.h> @@ -45,8 +53,6 @@ * starting point. #define this an be prepared for tons * of warnings and errors :) */ -#undef PMAZ_A - static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); static void dma_drain(struct NCR_ESP *esp); static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd * sp); @@ -62,7 +68,6 @@ static void dma_mmu_get_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd * sp); static void dma_mmu_get_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd * sp); static void dma_advance_sg(Scsi_Cmnd * sp); -#ifdef PMAZ_A static void pmaz_dma_drain(struct NCR_ESP *esp); static void pmaz_dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); static void pmaz_dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); @@ -70,10 +75,6 @@ static void pmaz_dma_ints_off(struct NCR_ESP *esp); static void pmaz_dma_ints_on(struct NCR_ESP *esp); static void pmaz_dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); static void pmaz_dma_mmu_get_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd * sp); -static void pmaz_dma_mmu_get_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd * sp); - -volatile int *scsi_pmaz_dma_ptr_tc; -#endif #define TC_ESP_RAM_SIZE 0x20000 #define ESP_TGT_DMA_SIZE ((TC_ESP_RAM_SIZE/7) & ~(sizeof(int)-1)) @@ -83,9 +84,6 @@ volatile int *scsi_pmaz_dma_ptr_tc; #define TC_ESP_DMAR_WRITE 0x80000000 #define TC_ESP_DMA_ADDR(x) ((unsigned)(x) & TC_ESP_DMAR_MASK) -volatile unsigned char *scsi_pmaz_dma_ptrs_tc[ESP_NCMD]; -unsigned char scsi_pmaz_dma_buff_used[ESP_NCMD]; -unsigned char scsi_cur_buff = 1; /* Leave space for command buffer */ __u32 esp_virt_buffer; int scsi_current_length; @@ -105,20 +103,21 @@ volatile unsigned long *scsi_sdr1; static void scsi_dma_int(int, void *, struct pt_regs *); +static Scsi_Host_Template driver_template = SCSI_DEC_ESP; + +#include "scsi_module.c" + /***************************************************************** Detection */ int dec_esp_detect(Scsi_Host_Template * tpnt) { struct NCR_ESP *esp; struct ConfigDev *esp_dev; -#ifdef PMAZ_A - int slot, i; + int slot; unsigned long mem_start; - volatile unsigned char *buffer; -#endif if (IOASIC) { - esp_dev = 0; - esp = esp_allocate(tpnt, (void *) esp_dev); + esp_dev = 0; + esp = esp_allocate(tpnt, (void *) esp_dev); scsi_dma_ptr = (unsigned long *) (system_base + IOCTL + SCSI_DMA_P); scsi_next_ptr = (unsigned long *) (system_base + IOCTL + SCSI_DMA_BP); @@ -127,86 +126,89 @@ int dec_esp_detect(Scsi_Host_Template * tpnt) scsi_sdr0 = (unsigned long *) (system_base + IOCTL + SCSI_SDR0); scsi_sdr1 = (unsigned long *) (system_base + IOCTL + SCSI_SDR1); - /* Do command transfer with programmed I/O */ - esp->do_pio_cmds = 1; + /* Do command transfer with programmed I/O */ + esp->do_pio_cmds = 1; - /* Required functions */ - esp->dma_bytes_sent = &dma_bytes_sent; - esp->dma_can_transfer = &dma_can_transfer; - esp->dma_dump_state = &dma_dump_state; - esp->dma_init_read = &dma_init_read; - esp->dma_init_write = &dma_init_write; - esp->dma_ints_off = &dma_ints_off; - esp->dma_ints_on = &dma_ints_on; - esp->dma_irq_p = &dma_irq_p; - esp->dma_ports_p = &dma_ports_p; - esp->dma_setup = &dma_setup; - - /* Optional functions */ - esp->dma_barrier = 0; + /* Required functions */ + esp->dma_bytes_sent = &dma_bytes_sent; + esp->dma_can_transfer = &dma_can_transfer; + esp->dma_dump_state = &dma_dump_state; + esp->dma_init_read = &dma_init_read; + esp->dma_init_write = &dma_init_write; + esp->dma_ints_off = &dma_ints_off; + esp->dma_ints_on = &dma_ints_on; + esp->dma_irq_p = &dma_irq_p; + esp->dma_ports_p = &dma_ports_p; + esp->dma_setup = &dma_setup; + + /* Optional functions */ + esp->dma_barrier = 0; esp->dma_drain = &dma_drain; - esp->dma_invalidate = 0; - esp->dma_irq_entry = 0; - esp->dma_irq_exit = 0; - esp->dma_poll = 0; - esp->dma_reset = 0; - esp->dma_led_off = 0; - esp->dma_led_on = 0; - - /* virtual DMA functions */ - esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; - esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; + esp->dma_invalidate = 0; + esp->dma_irq_entry = 0; + esp->dma_irq_exit = 0; + esp->dma_poll = 0; + esp->dma_reset = 0; + esp->dma_led_off = 0; + esp->dma_led_on = 0; + + /* virtual DMA functions */ + esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; + esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; esp->dma_mmu_release_scsi_one = 0; esp->dma_mmu_release_scsi_sgl = 0; - esp->dma_advance_sg = &dma_advance_sg; + esp->dma_advance_sg = &dma_advance_sg; - /* SCSI chip speed */ + /* SCSI chip speed */ esp->cfreq = 25000000; - /* - * we don't give the address of DMA channel, but the number - * of DMA channel, so we can use the jazz DMA functions - * - */ - esp->dregs = JAZZ_SCSI_DMA; + /* + * we don't give the address of DMA channel, but the number + * of DMA channel, so we can use the jazz DMA functions + * + */ + esp->dregs = JAZZ_SCSI_DMA; - /* ESP register base */ + /* ESP register base */ esp->eregs = (struct ESP_regs *) (system_base + SCSI); - /* Set the command buffer */ + /* Set the command buffer */ esp->esp_command = (volatile unsigned char *) cmd_buffer; - /* get virtual dma address for command buffer */ + /* get virtual dma address for command buffer */ esp->esp_command_dvma = (__u32) KSEG1ADDR((volatile unsigned char *) cmd_buffer); esp->irq = SCSI_INT; + + esp->scsi_id = 7; + + /* Check for differential SCSI-bus */ + esp->diff = 0; + + esp_initialize(esp); + if (request_irq(esp->irq, esp_intr, SA_INTERRUPT, "NCR 53C94 SCSI", NULL)) goto err_dealloc; if (request_irq(SCSI_DMA_INT, scsi_dma_int, SA_INTERRUPT, "JUNKIO SCSI DMA", NULL)) goto err_free_irq; - - - esp->scsi_id = 7; - - /* Check for differential SCSI-bus */ - esp->diff = 0; - - esp_initialize(esp); - + } -#ifdef PMAZ_A if (TURBOCHANNEL) { - while ((slot = search_tc_card("PMAZ_AA")) >= 0) { + while ((slot = search_tc_card("PMAZ-AA")) >= 0) { claim_tc_card(slot); - mem_start = get_tc_base_addr(slot); esp_dev = 0; esp = esp_allocate(tpnt, (void *) esp_dev); + mem_start = get_tc_base_addr(slot); + + /* Store base addr into esp struct */ + esp->slot = mem_start; + esp->dregs = 0; esp->eregs = (struct ESP_regs *) (mem_start + DEC_SCSI_SREG); esp->do_pio_cmds = 1; @@ -217,16 +219,6 @@ int dec_esp_detect(Scsi_Host_Template * tpnt) /* get virtual dma address for command buffer */ esp->esp_command_dvma = (__u32) KSEG0ADDR((volatile unsigned char *) pmaz_cmd_buffer); - buffer = (volatile unsigned char *) (mem_start + DEC_SCSI_SRAM); - - scsi_pmaz_dma_ptr_tc = (volatile int *) (mem_start + DEC_SCSI_DMAREG); - - for (i = 0; i < ESP_NCMD; i++) { - scsi_pmaz_dma_ptrs_tc[i] = (volatile unsigned char *) (buffer + ESP_TGT_DMA_SIZE * i); - } - - scsi_pmaz_dma_buff_used[0] = 1; - esp->cfreq = get_tc_speed(); esp->irq = get_tc_irq_nr(slot); @@ -260,18 +252,17 @@ int dec_esp_detect(Scsi_Host_Template * tpnt) esp->dma_mmu_release_scsi_sgl = 0; esp->dma_advance_sg = 0; - if (request_irq(esp->irq, esp_intr, SA_INTERRUPT, - "PMAZ_AA", NULL)) { - esp_deallocate(esp); - release_tc_card(slot); - continue; - } + if (request_irq(esp->irq, esp_intr, SA_INTERRUPT, + "PMAZ_AA", NULL)) { + esp_deallocate(esp); + release_tc_card(slot); + continue; + } esp->scsi_id = 7; esp->diff = 0; esp_initialize(esp); } } -#endif if(nesps) { printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use); @@ -476,30 +467,35 @@ static void dma_advance_sg(Scsi_Cmnd * sp) sp->SCp.ptr = (char *) ((unsigned long) sp->SCp.buffer->dvma_address); } -#ifdef PMAZ_A - static void pmaz_dma_drain(struct NCR_ESP *esp) { memcpy((void *) (KSEG0ADDR(esp_virt_buffer)), - (void *) scsi_pmaz_dma_ptrs_tc[scsi_cur_buff], scsi_current_length); + (void *) ( esp->slot + DEC_SCSI_SRAM + ESP_TGT_DMA_SIZE), + scsi_current_length); } static void pmaz_dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) { + volatile int *dmareg = (volatile int *) (esp->slot + DEC_SCSI_DMAREG); if (length > ESP_TGT_DMA_SIZE) length = ESP_TGT_DMA_SIZE; - *scsi_pmaz_dma_ptr_tc = TC_ESP_DMA_ADDR(scsi_pmaz_dma_ptrs_tc[scsi_cur_buff]); + *dmareg = TC_ESP_DMA_ADDR(esp->slot + DEC_SCSI_SRAM + ESP_TGT_DMA_SIZE); + esp_virt_buffer = vaddress; scsi_current_length = length; } static void pmaz_dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) { - memcpy((void *)scsi_pmaz_dma_ptrs_tc[scsi_cur_buff], KSEG0ADDR((void *) vaddress), length); + volatile int *dmareg = (volatile int *) ( esp->slot + DEC_SCSI_DMAREG ); + + memcpy((void *) (esp->slot + DEC_SCSI_SRAM + ESP_TGT_DMA_SIZE), + KSEG0ADDR((void *) vaddress), length); - *scsi_pmaz_dma_ptr_tc = TC_ESP_DMAR_WRITE | TC_ESP_DMA_ADDR(scsi_pmaz_dma_ptrs_tc[scsi_cur_buff]); + *dmareg = TC_ESP_DMAR_WRITE | + TC_ESP_DMA_ADDR(esp->slot + DEC_SCSI_SRAM + ESP_TGT_DMA_SIZE); } @@ -524,18 +520,9 @@ static void pmaz_dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write } } -static void pmaz_dma_mmu_release_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd * sp) -{ - int x; - for (x = 1; x < 6; x++) - if (sp->SCp.have_data_in == PHYSADDR(scsi_pmaz_dma_ptrs_tc[x])) - scsi_pmaz_dma_buff_used[x] = 0; -} - static void pmaz_dma_mmu_get_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd * sp) { sp->SCp.have_data_in = (int) sp->SCp.ptr = (char *) KSEG0ADDR((sp->request_buffer)); } -#endif diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index ddf8c957caca..1ee77dba489e 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -1081,6 +1081,9 @@ static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u3 { struct adpt_device* d; + if(chan < 0 || chan >= MAX_CHANNEL) + return NULL; + if( pHba->channel[chan].device == NULL){ printk(KERN_DEBUG"Adaptec I2O RAID: Trying to find device before they are allocated\n"); return NULL; diff --git a/drivers/scsi/lasi700.c b/drivers/scsi/lasi700.c new file mode 100644 index 000000000000..0bd3a64e24ec --- /dev/null +++ b/drivers/scsi/lasi700.c @@ -0,0 +1,188 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* PARISC LASI driver for the 53c700 chip + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com +**----------------------------------------------------------------------------- +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +**----------------------------------------------------------------------------- + */ + +/* + * Many thanks to Richard Hirst <rhirst@linuxcare.com> for patiently + * debugging this driver on the parisc architecture and suggesting + * many improvements and bug fixes. + * + * Thanks also go to Linuxcare Inc. for providing several PARISC + * machines for me to debug the driver on. + */ + +#ifndef __hppa__ +#error "lasi700 only compiles on hppa architecture" +#endif + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/stat.h> +#include <linux/mm.h> +#include <linux/blk.h> +#include <linux/sched.h> +#include <linux/version.h> +#include <linux/config.h> +#include <linux/ioport.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/delay.h> +#include <asm/gsc.h> + +#include <linux/module.h> + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#include "lasi700.h" +#include "53c700.h" + +#ifdef MODULE + +char *lasi700; /* command line from insmod */ + +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("lasi700 SCSI Driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(lasi700, "s"); + +#endif + +#ifdef MODULE +#define ARG_SEP ' ' +#else +#define ARG_SEP ',' +#endif + +static unsigned long __initdata opt_base; +static int __initdata opt_irq; + +static int __init +param_setup(char *string) +{ + char *pos = string, *next; + + while(pos != NULL && (next = strchr(pos, ':')) != NULL) { + int val = (int)simple_strtoul(++next, NULL, 0); + + if(!strncmp(pos, "addr:", 5)) + opt_base = val; + else if(!strncmp(pos, "irq:", 4)) + opt_irq = val; + + if((pos = strchr(pos, ARG_SEP)) != NULL) + pos++; + } + return 1; +} + +#ifndef MODULE +__setup("lasi700=", param_setup); +#endif + +static Scsi_Host_Template __initdata *host_tpnt = NULL; +static int __initdata host_count = 0; +static struct parisc_device_id lasi700_scsi_tbl[] = { + LASI700_ID_TABLE, + { 0 } +}; + +MODULE_DEVICE_TABLE(parisc, lasi700_scsi_tbl); + +static struct parisc_driver lasi700_driver = LASI700_DRIVER; + +static int __init +lasi700_detect(Scsi_Host_Template *tpnt) +{ + host_tpnt = tpnt; + +#ifdef MODULE + if(lasi700) + param_setup(lasi700); +#endif + + register_parisc_driver(&lasi700_driver); + + return (host_count != 0); +} + +static int __init +lasi700_driver_callback(struct parisc_device *dev) +{ + unsigned long base = dev->hpa + LASI_SCSI_CORE_OFFSET; + int irq = busdevice_alloc_irq(dev); + struct Scsi_Host *host; + struct NCR_700_Host_Parameters *hostdata = + kmalloc(sizeof(struct NCR_700_Host_Parameters), + GFP_KERNEL); + if(hostdata == NULL) { + printk(KERN_ERR "lasi700: Failed to allocate host data\n"); + return 1; + } + memset(hostdata, 0, sizeof(struct NCR_700_Host_Parameters)); + if(request_mem_region(base, 64, "lasi700") == NULL) { + printk(KERN_ERR "lasi700: Failed to claim memory region\n"); + kfree(hostdata); + return 1; + } + hostdata->base = base; + hostdata->differential = 0; + hostdata->clock = LASI700_CLOCK; + hostdata->force_le_on_be = 1; + if((host = NCR_700_detect(host_tpnt, hostdata)) == NULL) { + kfree(hostdata); + release_mem_region(host->base, 64); + return 1; + } + host->irq = irq; + if(request_irq(irq, NCR_700_intr, SA_SHIRQ, "lasi700", host)) { + printk(KERN_ERR "lasi700: irq problem, detatching\n"); + scsi_unregister(host); + NCR_700_release(host); + return 1; + } + host_count++; + return 0; +} + +static int +lasi700_release(struct Scsi_Host *host) +{ + struct D700_Host_Parameters *hostdata = + (struct D700_Host_Parameters *)host->hostdata[0]; + + NCR_700_release(host); + kfree(hostdata); + free_irq(host->irq, host); + release_mem_region(host->base, 64); + return 1; +} + +static Scsi_Host_Template driver_template = LASI700_SCSI; + +#include "scsi_module.c" diff --git a/drivers/scsi/lasi700.h b/drivers/scsi/lasi700.h new file mode 100644 index 000000000000..2c2cee9cdd83 --- /dev/null +++ b/drivers/scsi/lasi700.h @@ -0,0 +1,57 @@ +/* -*- mode: c; c-basic-offset: 8 -*- */ + +/* PARISC LASI driver for the 53c700 chip + * + * Copyright (C) 2001 by James.Bottomley@HansenPartnership.com +**----------------------------------------------------------------------------- +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +** +**----------------------------------------------------------------------------- + */ + +#ifndef _LASI700_H +#define _LASI700_H + +static int lasi700_detect(Scsi_Host_Template *); +static int lasi700_driver_callback(struct parisc_device *dev); +static int lasi700_release(struct Scsi_Host *host); + + +#define LASI700_SCSI { \ + name: "LASI SCSI 53c700", \ + proc_name: "lasi700", \ + detect: lasi700_detect, \ + release: lasi700_release, \ + this_id: 7, \ +} + +#define LASI700_ID_TABLE { \ + hw_type: HPHW_FIO, \ + sversion: 0x071, \ + hversion: HVERSION_ANY_ID, \ + hversion_rev: HVERSION_REV_ANY_ID, \ +} + +#define LASI700_DRIVER { \ + name: "Lasi SCSI", \ + id_table: lasi700_scsi_tbl, \ + probe: lasi700_driver_callback,\ +} + +#define LASI700_CLOCK 25 +#define LASI_SCSI_CORE_OFFSET 0x100 + +#endif diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index eb21e36e61ba..802f1f625878 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -3138,16 +3138,18 @@ int megaraid_detect (Scsi_Host_Template * pHostTmpl) * First argument (major) to register_chrdev implies a dynamic major * number allocation. */ - major = register_chrdev (0, "megadev", &megadev_fops); + if (count) { + major = register_chrdev (0, "megadev", &megadev_fops); - /* - * Register the Shutdown Notification hook in kernel - */ - if (register_reboot_notifier (&mega_notifier)) { - printk ("MegaRAID Shutdown routine not registered!!\n"); - } + /* + * Register the Shutdown Notification hook in kernel + */ + if (register_reboot_notifier (&mega_notifier)) { + printk ("MegaRAID Shutdown routine not registered!!\n"); + } - init_MUTEX (&mimd_entry_mtx); + init_MUTEX (&mimd_entry_mtx); + } return count; } diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 0d9343e7fd60..91a1e0258d81 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -421,6 +421,7 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt) static unsigned char generic_sense[6] = {REQUEST_SENSE, 0, 0, 0, 255, 0}; unsigned char scsi_result0[256], *scsi_result = NULL; + int saved_result; ASSERT_LOCK(&io_request_lock, 0); @@ -446,6 +447,7 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt) memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); memset((void *) scsi_result, 0, 256); + saved_result = SCpnt->result; SCpnt->request_buffer = scsi_result; SCpnt->request_bufflen = 256; SCpnt->use_sg = 0; @@ -470,6 +472,7 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt) */ memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, sizeof(SCpnt->data_cmnd)); + SCpnt->result = saved_result; SCpnt->request_buffer = SCpnt->buffer; SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index d8c61f5537f5..bea96170a4df 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -563,16 +563,11 @@ static struct block_device_operations sd_fops = static struct gendisk sd_gendisk = { - SCSI_DISK0_MAJOR, /* Major number */ - "sd", /* Major name */ - 4, /* Bits to shift to get real from partition */ - 1 << 4, /* Number of partitions per real */ - NULL, /* hd struct */ - NULL, /* block sizes */ - 0, /* number */ - NULL, /* internal */ - NULL, /* next */ - &sd_fops, /* file operations */ + major: SCSI_DISK0_MAJOR, + major_name: "sd", + minor_shift: 4, + max_p: 1 << 4, + fops: &sd_fops, }; static struct gendisk *sd_gendisks = &sd_gendisk; @@ -1021,7 +1016,7 @@ static int sd_init_onedisk(int i) cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; cmd[2] = 0x3f; /* Get all pages */ - cmd[4] = 255; /* But we only want the 8 byte header */ + cmd[4] = 255; /* Ask for 255 bytes, even tho we want just the first 8 */ SRpnt->sr_cmd_len = 0; SRpnt->sr_sense_buffer[0] = 0; SRpnt->sr_sense_buffer[2] = 0; diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c index 50a7bb34d06e..a7b441d38447 100644 --- a/drivers/scsi/sgiwd93.c +++ b/drivers/scsi/sgiwd93.c @@ -281,18 +281,30 @@ int __init sgiwd93_detect(Scsi_Host_Template *SGIblows) sgiwd93_host->irq = SGI_WD93_0_IRQ; buf = (uchar *) get_free_page(GFP_KERNEL); + if (!buf) { + printk(KERN_WARNING "sgiwd93: Could not allocate memory for host0 buffer.\n"); + scsi_unregister(sgiwd93_host); + return 0; + } init_hpc_chain(buf); dma_cache_wback_inv((unsigned long) buf, PAGE_SIZE); /* HPC_SCSI_REG0 | 0x03 | KSEG1 */ - wd33c93_init(sgiwd93_host, (wd33c93_regs *) 0xbfbc0003, + wd33c93_init(sgiwd93_host, (wd33c93_regs *) KSEG1ADDR (0x1fbc0003), dma_setup, dma_stop, WD33C93_FS_16_20); hdata = (struct WD33C93_hostdata *)sgiwd93_host->hostdata; hdata->no_sync = 0; hdata->dma_bounce_buffer = (uchar *) (KSEG1ADDR(buf)); - dma_cache_wback_inv((unsigned long) buf, PAGE_SIZE); - request_irq(SGI_WD93_0_IRQ, sgiwd93_intr, 0, "SGI WD93", (void *) sgiwd93_host); + if (request_irq(SGI_WD93_0_IRQ, sgiwd93_intr, 0, "SGI WD93", (void *) sgiwd93_host)) { + printk(KERN_WARNING "sgiwd93: Could not register IRQ %d (for host 0).\n", SGI_WD93_0_IRQ); +#ifdef MODULE + wd33c93_release(); +#endif + free_page((unsigned long)buf); + scsi_unregister(sgiwd93_host); + return 0; + } /* set up second controller on the Indigo2 */ if(!sgi_guiness) { sgiwd93_host1 = scsi_register(SGIblows, sizeof(struct WD33C93_hostdata)); @@ -302,10 +314,16 @@ int __init sgiwd93_detect(Scsi_Host_Template *SGIblows) sgiwd93_host1->irq = SGI_WD93_1_IRQ; buf = (uchar *) get_free_page(GFP_KERNEL); + if (!buf) { + printk(KERN_WARNING "sgiwd93: Could not allocate memory for host1 buffer.\n"); + scsi_unregister(sgiwd93_host1); + called = 1; + return 1; /* We registered host0 so return success*/ + } init_hpc_chain(buf); dma_cache_wback_inv((unsigned long) buf, PAGE_SIZE); /* HPC_SCSI_REG1 | 0x03 | KSEG1 */ - wd33c93_init(sgiwd93_host1, (wd33c93_regs *) 0xbfbc8003, + wd33c93_init(sgiwd93_host1, (wd33c93_regs *) KSEG1ADDR (0x1fbc8003), dma_setup, dma_stop, WD33C93_FS_16_20); hdata1 = (struct WD33C93_hostdata *)sgiwd93_host1->hostdata; @@ -313,7 +331,15 @@ int __init sgiwd93_detect(Scsi_Host_Template *SGIblows) hdata1->dma_bounce_buffer = (uchar *) (KSEG1ADDR(buf)); dma_cache_wback_inv((unsigned long) buf, PAGE_SIZE); - request_irq(SGI_WD93_1_IRQ, sgiwd93_intr, 0, "SGI WD93", (void *) sgiwd93_host1); + if (request_irq(SGI_WD93_1_IRQ, sgiwd93_intr, 0, "SGI WD93", (void *) sgiwd93_host1)) { + printk(KERN_WARNING "sgiwd93: Could not allocate irq %d (for host1).\n", SGI_WD93_1_IRQ); +#ifdef MODULE + wd33c93_release(); +#endif + free_page((unsigned long)buf); + scsi_unregister(sgiwd93_host1); + /* Fall through since host0 registered OK */ + } } } diff --git a/drivers/sound/i810_audio.c b/drivers/sound/i810_audio.c index dac53001ccc3..3139a8c448ad 100644 --- a/drivers/sound/i810_audio.c +++ b/drivers/sound/i810_audio.c @@ -106,6 +106,7 @@ static int ftsodell=0; static int strict_clocking=0; static unsigned int clocking=48000; +static int spdif_locked=0; //#define DEBUG //#define DEBUG2 @@ -118,6 +119,11 @@ static unsigned int clocking=48000; #define I810_FMT_STEREO 2 #define I810_FMT_MASK 3 +#define SPDIF_ON 0x0004 +#define SURR_ON 0x0010 +#define CENTER_LFE_ON 0x0020 +#define VOL_MUTED 0x8000 + /* the 810's array of pointers to data buffers */ struct sg_item { @@ -325,6 +331,8 @@ struct i810_card { struct i810_state *states[NR_HW_CH]; u16 ac97_features; + u16 ac97_status; + u16 channels; /* hardware resources */ unsigned long iobase; @@ -401,11 +409,164 @@ static void i810_free_pcm_channel(struct i810_card *card, int channel) card->channel[channel].used=0; } +static int i810_valid_spdif_rate ( struct ac97_codec *codec, int rate ) +{ + unsigned long id = 0L; + + id = (i810_ac97_get(codec, AC97_VENDOR_ID1) << 16); + id |= i810_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; +#ifdef DEBUG + printk ( "i810_audio: codec = %s, codec_id = 0x%08lx\n", codec->name, id); +#endif + switch ( id ) { + case 0x41445361: /* AD1886 */ + if (rate == 48000) { + return 1; + } + break; + default: /* all other codecs, until we know otherwiae */ + if (rate == 48000 || rate == 44100 || rate == 32000) { + return 1; + } + break; + } + return (0); +} + +/* i810_set_spdif_output + * + * Configure the S/PDIF output transmitter. When we turn on + * S/PDIF, we turn off the analog output. This may not be + * the right thing to do. + * + * Assumptions: + * The DSP sample rate must already be set to a supported + * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. + */ +static void i810_set_spdif_output(struct i810_state *state, int slots, int rate) +{ + int vol; + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if(!(state->card->ac97_features & 4)) { +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n"); +#endif + state->card->ac97_status &= ~SPDIF_ON; + } else { + if ( slots == -1 ) { /* Turn off S/PDIF */ + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + + /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ + if ( !(state->card->ac97_status & VOL_MUTED) ) { + aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); + i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED)); + } + state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); + return; + } + + vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); + state->card->ac97_status = vol & VOL_MUTED; + + /* Set S/PDIF transmitter sample rate */ + aud_reg = i810_ac97_get(codec, AC97_SPDIF_CONTROL); + switch ( rate ) { + case 32000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; + break; + case 44100: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; + break; + case 48000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; + break; + default: +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: %d sample rate not supported by S/PDIF.\n", rate); +#endif + /* turn off S/PDIF */ + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + + i810_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); + + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_VRA | AC97_EA_SPDIF; + i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + state->card->ac97_status |= SPDIF_ON; + + /* Check to make sure the configuration is valid */ + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + if ( ! (aud_reg & 0x0400) ) { +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: S/PDIF transmitter configuration not valid (0x%04x).\n", aud_reg); +#endif + + /* turn off S/PDIF */ + i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + /* Mute the analog output */ + /* Should this only mute the PCM volume??? */ + i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED)); + } +} + +/* i810_set_dac_channels + * + * Configure the codec's multi-channel DACs + * + * The logic is backwards. Setting the bit to 1 turns off the DAC. + * + * What about the ICH? We currently configure it using the + * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC, + * does that imply that we want the ICH set to support + * these channels? + * + * TODO: + * vailidate that the codec really supports these DACs + * before turning them on. + */ +static void i810_set_dac_channels(struct i810_state *state, int channel) +{ + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK; + state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON); + + switch ( channel ) { + case 2: /* always enabled */ + break; + case 4: + aud_reg &= ~AC97_EA_PRJ; + state->card->ac97_status |= SURR_ON; + break; + case 6: + aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK); + state->card->ac97_status |= SURR_ON | CENTER_LFE_ON; + break; + default: + break; + } + i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + +} + + /* set playback sample rate */ static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate) { struct dmabuf *dmabuf = &state->dmabuf; - u32 dacp, new_rate; + u32 new_rate; struct ac97_codec *codec=state->card->ac97_codec[0]; if(!(state->card->ac97_features&0x0001)) @@ -445,7 +606,7 @@ static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int ra static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate) { struct dmabuf *dmabuf = &state->dmabuf; - u32 dacp, new_rate; + u32 new_rate; struct ac97_codec *codec=state->card->ac97_codec[0]; if(!(state->card->ac97_features&0x0001)) @@ -1359,7 +1520,9 @@ static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long flags; audio_buf_info abinfo; count_info cinfo; - int val, mapped, ret; + unsigned int i_glob_cnt; + int val = 0, mapped, ret; + struct ac97_codec *codec = state->card->ac97_codec[0]; mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) || ((file->f_mode & FMODE_READ) && dmabuf->mapped); @@ -1412,11 +1575,31 @@ static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EFAULT; if (val >= 0) { if (file->f_mode & FMODE_WRITE) { - stop_dac(state); - dmabuf->ready = 0; - spin_lock_irqsave(&state->card->lock, flags); - i810_set_dac_rate(state, val); - spin_unlock_irqrestore(&state->card->lock, flags); + if ( (state->card->ac97_status & SPDIF_ON) ) { /* S/PDIF Enabled */ + /* AD1886 only supports 48000, need to check that */ + if ( i810_valid_spdif_rate ( codec, val ) ) { + /* Set DAC rate */ + i810_set_spdif_output ( state, -1, 0 ); + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + i810_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, val ); + if ( ! (state->card->ac97_status & SPDIF_ON) ) { + val = dmabuf->rate; + } + } else { /* Not a valid rate for S/PDIF, ignore it */ + val = dmabuf->rate; + } + } else { + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + i810_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } } if (file->f_mode & FMODE_READ) { stop_adc(state); @@ -1467,7 +1650,18 @@ static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, #ifdef DEBUG printk("SNDCTL_DSP_SETFMT\n"); #endif - return put_user(AFMT_S16_LE, (int *)arg); + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch ( val ) { + case AFMT_S16_LE: + break; + case AFMT_QUERY: + default: + val = AFMT_S16_LE; + break; + } + return put_user(val, (int *)arg); case SNDCTL_DSP_CHANNELS: #ifdef DEBUG @@ -1477,14 +1671,61 @@ static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EFAULT; if (val > 0) { - if (dmabuf->enable & DAC_RUNNING) { + if (dmabuf->enable & DAC_RUNNING) { stop_dac(state); } if (dmabuf->enable & ADC_RUNNING) { stop_adc(state); } + } else { + return put_user(state->card->channels, (int *)arg); } - return put_user(2, (int *)arg); + + /* ICH and ICH0 only support 2 channels */ + if ( state->card->pci_id == 0x2415 || state->card->pci_id == 0x2425 ) + return put_user(2, (int *)arg); + + /* Multi-channel support was added with ICH2. Bits in */ + /* Global Status and Global Control register are now */ + /* used to indicate this. */ + + i_glob_cnt = inl(state->card->iobase + GLOB_CNT); + + /* Current # of channels enabled */ + if ( i_glob_cnt & 0x0100000 ) + ret = 4; + else if ( i_glob_cnt & 0x0200000 ) + ret = 6; + else + ret = 2; + + switch ( val ) { + case 2: /* 2 channels is always supported */ + outl(state->card->iobase + GLOB_CNT, (i_glob_cnt & 0xcfffff)); + /* Do we need to change mixer settings???? */ + break; + case 4: /* Supported on some chipsets, better check first */ + if ( state->card->channels >= 4 ) { + outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0100000)); + /* Do we need to change mixer settings??? */ + } else { + val = ret; + } + break; + case 6: /* Supported on some chipsets, better check first */ + if ( state->card->channels >= 6 ) { + outl(state->card->iobase + GLOB_CNT, ((i_glob_cnt & 0xcfffff) | 0x0200000)); + /* Do we need to change mixer settings??? */ + } else { + val = ret; + } + break; + default: /* nothing else is ever supported by the chipset */ + val = ret; + break; + } + + return put_user(val, (int *)arg); case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */ /* we update the swptr to the end of the last sg segment then return */ @@ -1731,6 +1972,148 @@ static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, #endif return put_user(AFMT_S16_LE, (int *)arg); + case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETSPDIF\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* Check to make sure the codec supports S/PDIF transmitter */ + + if((state->card->ac97_features & 4)) { + /* mask out the transmitter speed bits so the user can't set them */ + val &= ~0x3000; + + /* Add the current transmitter speed bits to the passed value */ + ret = i810_ac97_get(codec, AC97_SPDIF_CONTROL); + val |= (ret & 0x3000); + + i810_ac97_set(codec, AC97_SPDIF_CONTROL, val); + if(i810_ac97_get(codec, AC97_SPDIF_CONTROL) != val ) { + printk(KERN_ERR "i810_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val); + return -EFAULT; + } + } +#ifdef DEBUG + else + printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n"); +#endif + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_GETSPDIF\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* Check to make sure the codec supports S/PDIF transmitter */ + + if(!(state->card->ac97_features & 4)) { +#ifdef DEBUG + printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n"); +#endif + val = 0; + } else { + val = i810_ac97_get(codec, AC97_SPDIF_CONTROL); + } + //return put_user((val & 0xcfff), (int *)arg); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETCHANNELMASK: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCHANNELMASK\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* Based on AC'97 DAC support, not ICH hardware */ + val = DSP_BIND_FRONT; + if ( state->card->ac97_features & 0x0004 ) + val |= DSP_BIND_SPDIF; + + if ( state->card->ac97_features & 0x0080 ) + val |= DSP_BIND_SURR; + if ( state->card->ac97_features & 0x0140 ) + val |= DSP_BIND_CENTER_LFE; + + return put_user(val, (int *)arg); + + case SNDCTL_DSP_BIND_CHANNEL: +#ifdef DEBUG + printk("SNDCTL_DSP_BIND_CHANNEL\n"); +#endif + if (get_user(val, (int *)arg)) + return -EFAULT; + if ( val == DSP_BIND_QUERY ) { + val = DSP_BIND_FRONT; /* Always report this as being enabled */ + if ( state->card->ac97_status & SPDIF_ON ) + val |= DSP_BIND_SPDIF; + else { + if ( state->card->ac97_status & SURR_ON ) + val |= DSP_BIND_SURR; + if ( state->card->ac97_status & CENTER_LFE_ON ) + val |= DSP_BIND_CENTER_LFE; + } + } else { /* Not a query, set it */ + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if ( dmabuf->enable == DAC_RUNNING ) { + stop_dac(state); + } + if ( val & DSP_BIND_SPDIF ) { /* Turn on SPDIF */ + /* Ok, this should probably define what slots + * to use. For now, we'll only set it to the + * defaults: + * + * non multichannel codec maps to slots 3&4 + * 2 channel codec maps to slots 7&8 + * 4 channel codec maps to slots 6&9 + * 6 channel codec maps to slots 10&11 + * + * there should be some way for the app to + * select the slot assignment. + */ + + i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, dmabuf->rate ); + if ( !(state->card->ac97_status & SPDIF_ON) ) + val &= ~DSP_BIND_SPDIF; + } else { + int mask; + int channels; + + /* Turn off S/PDIF if it was on */ + if ( state->card->ac97_status & SPDIF_ON ) + i810_set_spdif_output ( state, -1, 0 ); + + mask = val & (DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE); + switch (mask) { + case DSP_BIND_FRONT: + channels = 2; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR: + channels = 4; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE: + channels = 6; + break; + default: + val = DSP_BIND_FRONT; + channels = 2; + break; + } + i810_set_dac_channels ( state, channels ); + + /* check that they really got turned on */ + if ( !state->card->ac97_status & SURR_ON ) + val &= ~DSP_BIND_SURR; + if ( !state->card->ac97_status & CENTER_LFE_ON ) + val &= ~DSP_BIND_CENTER_LFE; + } + } + return put_user(val, (int *)arg); + case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: @@ -1796,7 +2179,15 @@ found_virt: card->states[i] = NULL;; return -EBUSY; } - i810_set_dac_rate(state, 8000); + /* Initialize to 8kHz? What if we don't support 8kHz? */ + /* Let's change this to check for S/PDIF stuff */ + + if ( spdif_locked ) { + i810_set_dac_rate(state, spdif_locked); + i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked); + } else { + i810_set_dac_rate(state, 8000); + } dmabuf->trigger |= PCM_ENABLE_OUTPUT; } @@ -1866,20 +2257,23 @@ static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) { struct i810_card *card = dev->private_data; int count = 100; + u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); while(count-- && (inb(card->iobase + CAS) & 1)) udelay(1); - return inw(card->ac97base + (reg&0x7f)); + + return inw(card->ac97base + reg_set); } static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) { struct i810_card *card = dev->private_data; int count = 100; + u8 reg_set = ((dev->id)?((reg&0x7f)|0x80):(reg&0x7f)); while(count-- && (inb(card->iobase + CAS) & 1)) udelay(1); - outw(data, card->ac97base + (reg&0x7f)); + outw(data, card->ac97base + reg_set); } @@ -1920,7 +2314,7 @@ static /*const*/ struct file_operations i810_mixer_fops = { static int __init i810_ac97_init(struct i810_card *card) { int num_ac97 = 0; - int ready_2nd = 0; + int total_channels = 0; struct ac97_codec *codec; u16 eid; int i=0; @@ -1952,10 +2346,38 @@ static int __init i810_ac97_init(struct i810_card *card) current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/5); + + /* Number of channels supported */ + /* What about the codec? Just because the ICH supports */ + /* multiple channels doesn't mean the codec does. */ + /* we'll have to modify this in the codec section below */ + /* to reflect what the codec has. */ + /* ICH and ICH0 only support 2 channels so don't bother */ + /* to check.... */ + + card->channels = 2; + reg = inl(card->iobase + GLOB_STA); + if ( reg & 0x0200000 ) + card->channels = 6; + else if ( reg & 0x0100000 ) + card->channels = 4; + printk("i810_audio: Audio Controller supports %d channels.\n", card->channels); inw(card->ac97base); for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + + /* The ICH programmer's reference says you should */ + /* check the ready status before probing. So we chk */ + /* What do we do if it's not ready? Wait and try */ + /* again, or abort? */ + reg = inl(card->iobase + GLOB_STA); + if (!(reg & (0x100 << num_ac97))) { + if(num_ac97 == 0) + printk(KERN_ERR "i810_audio: Primary codec not ready.\n"); + break; /* I think this works, if not ready stop */ + } + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) return -ENOMEM; memset(codec, 0, sizeof(struct ac97_codec)); @@ -1983,6 +2405,9 @@ static int __init i810_ac97_init(struct i810_card *card) schedule_timeout(HZ/20); } + /* Store state information about S/PDIF transmitter */ + card->ac97_status = 0; + /* Don't attempt to get eid until powerup is complete */ eid = i810_ac97_get(codec, AC97_EXTENDED_ID); @@ -2014,6 +2439,63 @@ static int __init i810_ac97_init(struct i810_card *card) } } + /* Determine how many channels the codec(s) support */ + /* - The primary codec always supports 2 */ + /* - If the codec supports AMAP, surround DACs will */ + /* automaticlly get assigned to slots. */ + /* * Check for surround DACs and increment if */ + /* found. */ + /* - Else check if the codec is revision 2.2 */ + /* * If surround DACs exist, assign them to slots */ + /* and increment channel count. */ + + /* All of this only applies to ICH2 and above. ICH */ + /* and ICH0 only support 2 channels. ICH2 will only */ + /* support multiple codecs in a "split audio" config. */ + /* as described above. */ + + /* TODO: Remove all the debugging messages! */ + + if((eid & 0xc000) == 0) /* primary codec */ + total_channels += 2; + + if(eid & 0x200) { /* GOOD, AMAP support */ + if (eid & 0x0080) /* L/R Surround channels */ + total_channels += 2; + if (eid & 0x0140) /* LFE and Center channels */ + total_channels += 2; + printk("i810_audio: AC'97 codec %d supports AMAP, total channels = %d\n", num_ac97, total_channels); + } else if (eid & 0x0400) { /* this only works on 2.2 compliant codecs */ + eid &= 0xffcf; + if((eid & 0xc000) != 0) { + switch ( total_channels ) { + case 2: + /* Set dsa1, dsa0 to 01 */ + eid |= 0x0010; + break; + case 4: + /* Set dsa1, dsa0 to 10 */ + eid |= 0x0020; + break; + case 6: + /* Set dsa1, dsa0 to 11 */ + eid |= 0x0030; + break; + } + total_channels += 2; + } + i810_ac97_set(codec, AC97_EXTENDED_ID, eid); + eid = i810_ac97_get(codec, AC97_EXTENDED_ID); + printk("i810_audio: AC'97 codec %d, new EID value = 0x%04x\n", num_ac97, eid); + if (eid & 0x0080) /* L/R Surround channels */ + total_channels += 2; + if (eid & 0x0140) /* LFE and Center channels */ + total_channels += 2; + printk("i810_audio: AC'97 codec %d, DAC map configured, total channels = %d\n", num_ac97, total_channels); + } else { + printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", num_ac97, total_channels); + } + if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { printk(KERN_ERR "i810_audio: couldn't register mixer!\n"); kfree(codec); @@ -2021,11 +2503,11 @@ static int __init i810_ac97_init(struct i810_card *card) } card->ac97_codec[num_ac97] = codec; - - /* if there is no secondary codec at all, don't probe any more */ - if (!ready_2nd) - return num_ac97+1; } + + /* pick the minimum of channels supported by ICHx or codec(s) */ + card->channels = (card->channels > total_channels)?total_channels:card->channels; + return num_ac97; } @@ -2221,6 +2703,7 @@ MODULE_DESCRIPTION("Intel 810 audio support"); MODULE_PARM(ftsodell, "i"); MODULE_PARM(clocking, "i"); MODULE_PARM(strict_clocking, "i"); +MODULE_PARM(spdif_locked, "i"); #define I810_MODULE_NAME "intel810_audio" @@ -2250,6 +2733,15 @@ static int __init i810_init_module (void) if(clocking == 48000) { i810_configure_clocking(); } + if(spdif_locked > 0 ) { + if(spdif_locked == 32000 || spdif_locked == 44100 || spdif_locked == 48000) { + printk("i810_audio: Enabling S/PDIF at sample rate %dHz.\n", spdif_locked); + } else { + printk("i810_audio: S/PDIF can only be locked to 32000, 441000, or 48000Hz.\n"); + spdif_locked = 0; + } + } + return 0; } diff --git a/drivers/sound/opl3sa2.c b/drivers/sound/opl3sa2.c index 7f2b0d347809..51c5cf29e6f5 100644 --- a/drivers/sound/opl3sa2.c +++ b/drivers/sound/opl3sa2.c @@ -810,7 +810,7 @@ static void __exit unload_opl3sa2(struct address_info* hw_config, int card) struct isapnp_device_id isapnp_opl3sa2_list[] __initdata = { { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), - NULL }, + 0 }, {0} }; diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index e0bfdc9541d7..83e7cd921f19 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -145,7 +145,7 @@ static void __sound_remove_unit(struct sound_unit **list, int unit) * This lock guards the sound loader list. */ -spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED; /* * Allocate the controlling structure and add it to the sound driver diff --git a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c index cf8f040ee493..1ea4615e0173 100644 --- a/drivers/sound/via82cxxx_audio.c +++ b/drivers/sound/via82cxxx_audio.c @@ -858,6 +858,7 @@ static void via_chan_pcm_fmt (struct via_channel *chan, int reset) /** * via_chan_clear - Stop DMA channel operation, and reset pointers + * @card: the chip to accessed * @chan: Channel to be cleared * * Call via_chan_stop to halt DMA operations, and then resets diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c index f118838131d0..e5d934b5861c 100644 --- a/drivers/telephony/ixj.c +++ b/drivers/telephony/ixj.c @@ -6693,7 +6693,10 @@ static int ixj_ioctl(struct inode *inode, struct file *file_p, unsigned int cmd, retval = ixj_init_filter_raw(j, &jfr); break; case IXJCTL_GET_FILTER_HIST: - retval = j->filter_hist[arg]; + if(arg<0||arg>3) + retval = -EINVAL; + else + retval = j->filter_hist[arg]; break; case IXJCTL_INIT_TONE: copy_from_user(&ti, (char *) arg, sizeof(ti)); diff --git a/drivers/video/radeon.h b/drivers/video/radeon.h index 447fdb08b777..7e56c8a5a9a4 100644 --- a/drivers/video/radeon.h +++ b/drivers/video/radeon.h @@ -7,6 +7,7 @@ #define PCI_DEVICE_ID_RADEON_QE 0x5145 #define PCI_DEVICE_ID_RADEON_QF 0x5146 #define PCI_DEVICE_ID_RADEON_QG 0x5147 +#define PCI_DEVICE_ID_RADEON_VE 0x5159 #define RADEON_REGSIZE 0x4000 diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c index d7c091559c8e..764c439c61c5 100644 --- a/drivers/video/radeonfb.c +++ b/drivers/video/radeonfb.c @@ -12,13 +12,14 @@ * 2001-07-05 fixed scrolling issues, engine initialization, * and minor mode tweaking, 0.0.9 * + * 2001-09-07 Radeon VE support * * Special thanks to ATI DevRel team for their hardware donations. * */ -#define RADEON_VERSION "0.0.9" +#define RADEON_VERSION "0.0.10" #include <linux/config.h> @@ -62,7 +63,8 @@ enum radeon_chips { RADEON_QD, RADEON_QE, RADEON_QF, - RADEON_QG + RADEON_QG, + RADEON_VE }; @@ -71,6 +73,7 @@ static struct pci_device_id radeonfb_pci_table[] __devinitdata = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_RADEON_QE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QE}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_RADEON_QF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QF}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_RADEON_QG, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QG}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_RADEON_VE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_VE}, { 0, } }; MODULE_DEVICE_TABLE(pci, radeonfb_pci_table); @@ -639,6 +642,9 @@ static int radeonfb_pci_register (struct pci_dev *pdev, case PCI_DEVICE_ID_RADEON_QG: strcpy(rinfo->name, "Radeon QG "); break; + case PCI_DEVICE_ID_RADEON_VE: + strcpy(rinfo->name, "Radeon VE "); + break; default: return -ENODEV; } @@ -754,7 +760,7 @@ static int radeonfb_pci_register (struct pci_dev *pdev, radeon_engine_init (rinfo); } - printk ("radeonfb: ATI Radeon %s %d MB\n", rinfo->ram_type, + printk ("radeonfb: ATI %s %d MB\n",rinfo->name, (rinfo->video_ram/(1024*1024))); return 0; |
