diff options
241 files changed, 8350 insertions, 29910 deletions
diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 38f88b6ae40..8c5698a8c2e 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -643,4 +643,15 @@ X!Idrivers/video/console/fonts.c !Edrivers/spi/spi.c </chapter> + <chapter id="splice"> + <title>splice API</title> + <para>) + splice is a method for moving blocks of data around inside the + kernel, without continually transferring it between the kernel + and user space. + </para> +!Iinclude/linux/splice.h +!Ffs/splice.c + </chapter> + </book> diff --git a/Documentation/block/barrier.txt b/Documentation/block/barrier.txt index a272c3db809..7d279f2f5bb 100644 --- a/Documentation/block/barrier.txt +++ b/Documentation/block/barrier.txt @@ -82,23 +82,12 @@ including draining and flushing. typedef void (prepare_flush_fn)(request_queue_t *q, struct request *rq); int blk_queue_ordered(request_queue_t *q, unsigned ordered, - prepare_flush_fn *prepare_flush_fn, - unsigned gfp_mask); - -int blk_queue_ordered_locked(request_queue_t *q, unsigned ordered, - prepare_flush_fn *prepare_flush_fn, - unsigned gfp_mask); - -The only difference between the two functions is whether or not the -caller is holding q->queue_lock on entry. The latter expects the -caller is holding the lock. + prepare_flush_fn *prepare_flush_fn); @q : the queue in question @ordered : the ordered mode the driver/device supports @prepare_flush_fn : this function should prepare @rq such that it flushes cache to physical medium when executed -@gfp_mask : gfp_mask used when allocating data structures - for ordered processing For example, SCSI disk driver's prepare_flush_fn looks like the following. @@ -106,9 +95,10 @@ following. static void sd_prepare_flush(request_queue_t *q, struct request *rq) { memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->flags |= REQ_BLOCK_PC; + rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->timeout = SD_TIMEOUT; rq->cmd[0] = SYNCHRONIZE_CACHE; + rq->cmd_len = 10; } The following seven ordered modes are supported. The following table diff --git a/Documentation/power_supply_class.txt b/Documentation/power_supply_class.txt new file mode 100644 index 00000000000..9758cf433c0 --- /dev/null +++ b/Documentation/power_supply_class.txt @@ -0,0 +1,167 @@ +Linux power supply class +======================== + +Synopsis +~~~~~~~~ +Power supply class used to represent battery, UPS, AC or DC power supply +properties to user-space. + +It defines core set of attributes, which should be applicable to (almost) +every power supply out there. Attributes are available via sysfs and uevent +interfaces. + +Each attribute has well defined meaning, up to unit of measure used. While +the attributes provided are believed to be universally applicable to any +power supply, specific monitoring hardware may not be able to provide them +all, so any of them may be skipped. + +Power supply class is extensible, and allows to define drivers own attributes. +The core attribute set is subject to the standard Linux evolution (i.e. +if it will be found that some attribute is applicable to many power supply +types or their drivers, it can be added to the core set). + +It also integrates with LED framework, for the purpose of providing +typically expected feedback of battery charging/fully charged status and +AC/USB power supply online status. (Note that specific details of the +indication (including whether to use it at all) are fully controllable by +user and/or specific machine defaults, per design principles of LED +framework). + + +Attributes/properties +~~~~~~~~~~~~~~~~~~~~~ +Power supply class has predefined set of attributes, this eliminates code +duplication across drivers. Power supply class insist on reusing its +predefined attributes *and* their units. + +So, userspace gets predictable set of attributes and their units for any +kind of power supply, and can process/present them to a user in consistent +manner. Results for different power supplies and machines are also directly +comparable. + +See drivers/power/ds2760_battery.c and drivers/power/pda_power.c for the +example how to declare and handle attributes. + + +Units +~~~~~ +Quoting include/linux/power_supply.h: + + All voltages, currents, charges, energies, time and temperatures in µV, + µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise + stated. It's driver's job to convert its raw values to units in which + this class operates. + + +Attributes/properties detailed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +~ ~ ~ ~ ~ ~ ~ Charge/Energy/Capacity - how to not confuse ~ ~ ~ ~ ~ ~ ~ +~ ~ +~ Because both "charge" (µAh) and "energy" (µWh) represents "capacity" ~ +~ of battery, this class distinguish these terms. Don't mix them! ~ +~ ~ +~ CHARGE_* attributes represents capacity in µAh only. ~ +~ ENERGY_* attributes represents capacity in µWh only. ~ +~ CAPACITY attribute represents capacity in *percents*, from 0 to 100. ~ +~ ~ +~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ + +Postfixes: +_AVG - *hardware* averaged value, use it if your hardware is really able to +report averaged values. +_NOW - momentary/instantaneous values. + +STATUS - this attribute represents operating status (charging, full, +discharging (i.e. powering a load), etc.). This corresponds to +BATTERY_STATUS_* values, as defined in battery.h. + +HEALTH - represents health of the battery, values corresponds to +POWER_SUPPLY_HEALTH_*, defined in battery.h. + +VOLTAGE_MAX_DESIGN, VOLTAGE_MIN_DESIGN - design values for maximal and +minimal power supply voltages. Maximal/minimal means values of voltages +when battery considered "full"/"empty" at normal conditions. Yes, there is +no direct relation between voltage and battery capacity, but some dumb +batteries use voltage for very approximated calculation of capacity. +Battery driver also can use this attribute just to inform userspace +about maximal and minimal voltage thresholds of a given battery. + +CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when +battery considered full/empty. + +ENERGY_FULL_DESIGN, ENERGY_EMPTY_DESIGN - same as above but for energy. + +CHARGE_FULL, CHARGE_EMPTY - These attributes means "last remembered value +of charge when battery became full/empty". It also could mean "value of +charge when battery considered full/empty at given conditions (temperature, +age)". I.e. these attributes represents real thresholds, not design values. + +ENERGY_FULL, ENERGY_EMPTY - same as above but for energy. + +CAPACITY - capacity in percents. +CAPACITY_LEVEL - capacity level. This corresponds to +POWER_SUPPLY_CAPACITY_LEVEL_*. + +TEMP - temperature of the power supply. +TEMP_AMBIENT - ambient temperature. + +TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e. +while battery powers a load) +TIME_TO_FULL - seconds left for battery to be considered full (i.e. +while battery is charging) + + +Battery <-> external power supply interaction +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Often power supplies are acting as supplies and supplicants at the same +time. Batteries are good example. So, batteries usually care if they're +externally powered or not. + +For that case, power supply class implements notification mechanism for +batteries. + +External power supply (AC) lists supplicants (batteries) names in +"supplied_to" struct member, and each power_supply_changed() call +issued by external power supply will notify supplicants via +external_power_changed callback. + + +QA +~~ +Q: Where is POWER_SUPPLY_PROP_XYZ attribute? +A: If you cannot find attribute suitable for your driver needs, feel free + to add it and send patch along with your driver. + + The attributes available currently are the ones currently provided by the + drivers written. + + Good candidates to add in future: model/part#, cycle_time, manufacturer, + etc. + + +Q: I have some very specific attribute (e.g. battery color), should I add + this attribute to standard ones? +A: Most likely, no. Such attribute can be placed in the driver itself, if + it is useful. Of course, if the attribute in question applicable to + large set of batteries, provided by many drivers, and/or comes from + some general battery specification/standard, it may be a candidate to + be added to the core attribute set. + + +Q: Suppose, my battery monitoring chip/firmware does not provides capacity + in percents, but provides charge_{now,full,empty}. Should I calculate + percentage capacity manually, inside the driver, and register CAPACITY + attribute? The same question about time_to_empty/time_to_full. +A: Most likely, no. This class is designed to export properties which are + directly measurable by the specific hardware available. + + Inferring not available properties using some heuristics or mathematical + model is not subject of work for a battery driver. Such functionality + should be factored out, and in fact, apm_power, the driver to serve + legacy APM API on top of power supply class, uses a simple heuristic of + approximating remaining battery capacity based on its charge, current, + voltage and so on. But full-fledged battery model is likely not subject + for kernel at all, as it would require floating point calculation to deal + with things like differential equations and Kalman filters. This is + better be handled by batteryd/libbattery, yet to be written. diff --git a/MAINTAINERS b/MAINTAINERS index 2c1dfb27161..bc272bf0ff9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1856,7 +1856,7 @@ W: http://www.openib.org/ T: git kernel.org:/pub/scm/linux/kernel/git/roland/infiniband.git S: Supported -INPUT (KEYBOARD, MOUSE, JOYSTICK) DRIVERS +INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS P: Dmitry Torokhov M: dmitry.torokhov@gmail.com M: dtor@mail.ru diff --git a/arch/s390/crypto/crypt_s390.h b/arch/s390/crypto/crypt_s390.h index 2775d261833..95f5160df27 100644 --- a/arch/s390/crypto/crypt_s390.h +++ b/arch/s390/crypto/crypt_s390.h @@ -24,7 +24,7 @@ #define CRYPT_S390_PRIORITY 300 #define CRYPT_S390_COMPOSITE_PRIORITY 400 -/* s930 cryptographic operations */ +/* s390 cryptographic operations */ enum crypt_s390_operations { CRYPT_S390_KM = 0x0100, CRYPT_S390_KMC = 0x0200, diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 50538e54561..e6289ee74ec 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -171,37 +171,6 @@ static inline int memory_fast_detect(void) } #endif -#define ADDR2G (1UL << 31) - -static noinline __init unsigned long sclp_memory_detect(void) -{ - struct sclp_readinfo_sccb *sccb; - unsigned long long memsize; - - sccb = &s390_readinfo_sccb; - - if (sccb->header.response_code != 0x10) - return 0; - - if (sccb->rnsize) - memsize = sccb->rnsize << 20; - else - memsize = sccb->rnsize2 << 20; - if (sccb->rnmax) - memsize *= sccb->rnmax; - else - memsize *= sccb->rnmax2; -#ifndef CONFIG_64BIT - /* - * Can't deal with more than 2G in 31 bit addressing mode, so - * limit the value in order to avoid strange side effects. - */ - if (memsize > ADDR2G) - memsize = ADDR2G; -#endif - return (unsigned long) memsize; -} - static inline __init unsigned long __tprot(unsigned long addr) { int cc = -1; @@ -218,6 +187,7 @@ static inline __init unsigned long __tprot(unsigned long addr) /* Checking memory in 128KB increments. */ #define CHUNK_INCR (1UL << 17) +#define ADDR2G (1UL << 31) static noinline __init void find_memory_chunks(unsigned long memsize) { @@ -293,7 +263,7 @@ static noinline __init void setup_lowcore_early(void) */ void __init startup_init(void) { - unsigned long memsize; + unsigned long long memsize; ipl_save_parameters(); clear_bss_section(); @@ -305,8 +275,17 @@ void __init startup_init(void) sort_main_extable(); setup_lowcore_early(); sclp_readinfo_early(); + sclp_facilities_detect(); memsize = sclp_memory_detect(); +#ifndef CONFIG_64BIT + /* + * Can't deal with more than 2G in 31 bit addressing mode, so + * limit the value in order to avoid strange side effects. + */ + if (memsize > ADDR2G) + memsize = ADDR2G; +#endif if (memory_fast_detect() < 0) - find_memory_chunks(memsize); + find_memory_chunks((unsigned long) memsize); lockdep_on(); } diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 6234c6978a1..bc7ff3658c3 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -107,6 +107,11 @@ STACK_SIZE = 1 << STACK_SHIFT l %r13,__LC_SVC_NEW_PSW+4 # load &system_call to %r13 .endm + .macro SAVE_ALL_SVC psworg,savearea + la %r12,\psworg + l %r15,__LC_KERNEL_STACK # problem state -> load ksp + .endm + .macro SAVE_ALL_SYNC psworg,savearea la %r12,\psworg tm \psworg+1,0x01 # test problem state bit @@ -218,7 +223,7 @@ system_call: STORE_TIMER __LC_SYNC_ENTER_TIMER sysc_saveall: SAVE_ALL_BASE __LC_SAVE_AREA - SAVE_ALL_SYNC __LC_SVC_OLD_PSW,__LC_SAVE_AREA + SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA CREATE_STACK_FRAME __LC_SVC_OLD_PSW,__LC_SAVE_AREA lh %r7,0x8a # get svc number from lowcore #ifdef CONFIG_VIRT_CPU_ACCOUNTING diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 685f11faa4b..2a7b1304418 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -99,6 +99,11 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK | _TIF_NEED_RESCHED | \ larl %r13,system_call .endm + .macro SAVE_ALL_SVC psworg,savearea + la %r12,\psworg + lg %r15,__LC_KERNEL_STACK # problem state -> load ksp + .endm + .macro SAVE_ALL_SYNC psworg,savearea la %r12,\psworg tm \psworg+1,0x01 # test problem state bit @@ -207,7 +212,7 @@ system_call: STORE_TIMER __LC_SYNC_ENTER_TIMER sysc_saveall: SAVE_ALL_BASE __LC_SAVE_AREA - SAVE_ALL_SYNC __LC_SVC_OLD_PSW,__LC_SAVE_AREA + SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA CREATE_STACK_FRAME __LC_SVC_OLD_PSW,__LC_SAVE_AREA llgh %r7,__LC_SVC_INT_CODE # get svc number from lowcore #ifdef CONFIG_VIRT_CPU_ACCOUNTING diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 367caf92ea7..82b131ddd7f 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -25,10 +25,6 @@ #define IPL_PARM_BLOCK_VERSION 0 -#define SCCB_VALID (s390_readinfo_sccb.header.response_code == 0x10) -#define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm) -#define SCCB_FLAG (s390_readinfo_sccb.flags) - #define IPL_UNKNOWN_STR "unknown" #define IPL_CCW_STR "ccw" #define IPL_FCP_STR "fcp" @@ -146,6 +142,8 @@ static struct ipl_parameter_block *dump_block_ccw; static enum shutdown_action on_panic_action = SHUTDOWN_STOP; +static struct sclp_ipl_info sclp_ipl_info; + int diag308(unsigned long subcode, void *addr) { register unsigned long _addr asm("0") = (unsigned long) addr; @@ -375,9 +373,9 @@ static ssize_t ipl_ccw_loadparm_show(struct kset *kset, char *page) { char loadparm[LOADPARM_LEN + 1] = {}; - if (!SCCB_VALID) + if (!sclp_ipl_info.is_valid) return sprintf(page, "#unknown#\n"); - memcpy(loadparm, SCCB_LOADPARM, LOADPARM_LEN); + memcpy(loadparm, &sclp_ipl_info.loadparm, LOADPARM_LEN); EBCASC(loadparm, LOADPARM_LEN); strstrip(loadparm); return sprintf(page, "%s\n", loadparm); @@ -910,9 +908,9 @@ static int __init reipl_ccw_init(void) reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; /* check if read scp info worked and set loadparm */ - if (SCCB_VALID) + if (sclp_ipl_info.is_valid) memcpy(reipl_block_ccw->ipl_info.ccw.load_param, - SCCB_LOADPARM, LOADPARM_LEN); + &sclp_ipl_info.loadparm, LOADPARM_LEN); else /* read scp info failed: set empty loadparm (EBCDIC blanks) */ memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, @@ -1007,7 +1005,7 @@ static int __init dump_fcp_init(void) { int rc; - if(!(SCCB_FLAG & 0x2) || !SCCB_VALID) + if (!sclp_ipl_info.has_dump) return 0; /* LDIPL DUMP is not installed */ if (!diag308_set_works) return 0; @@ -1088,6 +1086,7 @@ static int __init s390_ipl_init(void) { int rc; + sclp_get_ipl_info(&sclp_ipl_info); reipl_probe(); rc = ipl_init(); if (rc) diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index eb43c3b3126..441975b796f 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -93,8 +93,8 @@ void do_monitor_call(struct pt_regs *regs, long interruption_code) /* disable monitor call class 0 */ __ctl_clear_bit(8, 15); - atomic_notifier_call_chain(&idle_chain, CPU_NOT_IDLE, - (void *)(long) smp_processor_id()); + atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE, + (void *)(long) smp_processor_id()); } extern void s390_handle_mcck(void); @@ -115,7 +115,7 @@ static void default_idle(void) } rc = atomic_notifier_call_chain(&idle_chain, - CPU_IDLE, (void *)(long) cpu); + S390_CPU_IDLE, (void *)(long) cpu); if (rc != NOTIFY_OK && rc != NOTIFY_DONE) BUG(); if (rc != NOTIFY_OK) { diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 8ff2feaf9b0..182c085ae4d 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -410,58 +410,40 @@ EXPORT_SYMBOL(smp_ctl_clear_bit); unsigned int zfcpdump_prefix_array[NR_CPUS + 1] \ __attribute__((__section__(".data"))); -static void __init smp_get_save_areas(void) +static void __init smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) { - unsigned int cpu, cpu_num, rc; - __u16 boot_cpu_addr; - if (ipl_info.type != IPL_TYPE_FCP_DUMP) return; - boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; - cpu_num = 1; - for (cpu = 0; cpu <= 65535; cpu++) { - if ((u16) cpu == boot_cpu_addr) - continue; - __cpu_logical_map[1] = (__u16) cpu; - if (signal_processor(1, sigp_sense) == sigp_not_operational) - continue; - if (cpu_num >= NR_CPUS) { - printk("WARNING: Registers for cpu %i are not " - "saved, since dump kernel was compiled with" - "NR_CPUS=%i!\n", cpu_num, NR_CPUS); - continue; - } - zfcpdump_save_areas[cpu_num] = - alloc_bootmem(sizeof(union save_area)); - while (1) { - rc = signal_processor(1, sigp_stop_and_store_status); - if (rc != sigp_busy) - break; - cpu_relax(); - } - memcpy(zfcpdump_save_areas[cpu_num], - (void *)(unsigned long) store_prefix() + - SAVE_AREA_BASE, SAVE_AREA_SIZE); -#ifdef __s390x__ - /* copy original prefix register */ - zfcpdump_save_areas[cpu_num]->s390x.pref_reg = - zfcpdump_prefix_array[cpu_num]; -#endif - cpu_num++; + if (cpu >= NR_CPUS) { + printk(KERN_WARNING "Registers for cpu %i not saved since dump " + "kernel was compiled with NR_CPUS=%i\n", cpu, NR_CPUS); + return; } + zfcpdump_save_areas[cpu] = alloc_bootmem(sizeof(union save_area)); + __cpu_logical_map[1] = (__u16) phy_cpu; + while (signal_processor(1, sigp_stop_and_store_status) == sigp_busy) + cpu_relax(); + memcpy(zfcpdump_save_areas[cpu], + (void *)(unsigned long) store_prefix() + SAVE_AREA_BASE, + SAVE_AREA_SIZE); +#ifdef CONFIG_64BIT + /* copy original prefix register */ + zfcpdump_save_areas[cpu]->s390x.pref_reg = zfcpdump_prefix_array[cpu]; +#endif } union save_area *zfcpdump_save_areas[NR_CPUS + 1]; EXPORT_SYMBOL_GPL(zfcpdump_save_areas); #else -#define smp_get_save_areas() do { } while (0) -#endif + +static inline void smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) { } + +#endif /* CONFIG_ZFCPDUMP || CONFIG_ZFCPDUMP_MODULE */ /* * Lets check how many CPUs we have. */ - static unsigned int __init smp_count_cpus(void) { unsigned int cpu, num_cpus; @@ -470,7 +452,6 @@ static unsigned int __init smp_count_cpus(void) /* * cpu 0 is the boot cpu. See smp_prepare_boot_cpu. */ - boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; current_thread_info()->cpu = 0; num_cpus = 1; @@ -480,12 +461,11 @@ static unsigned int __init smp_count_cpus(void) __cpu_logical_map[1] = (__u16) cpu; if (signal_processor(1, sigp_sense) == sigp_not_operational) continue; + smp_get_save_area(num_cpus, cpu); num_cpus++; } - printk("Detected %d CPU's\n", (int) num_cpus); printk("Boot cpu address %2X\n", boot_cpu_addr); - return num_cpus; } @@ -606,7 +586,6 @@ void __init smp_setup_cpu_possible_map(void) { unsigned int phy_cpus, pos_cpus, cpu; - smp_get_save_areas(); phy_cpus = smp_count_cpus(); pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS); diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 9c2872a7cca..48dae49bc1e 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -226,10 +226,10 @@ static int nohz_idle_notify(struct notifier_block *self, unsigned long action, void *hcpu) { switch (action) { - case CPU_IDLE: + case S390_CPU_IDLE: stop_hz_timer(); break; - case CPU_NOT_IDLE: + case S390_CPU_NOT_IDLE: start_hz_timer(); break; } diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 1e1a6ee2cac..b6ed143e859 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -545,10 +545,10 @@ static int vtimer_idle_notify(struct notifier_block *self, unsigned long action, void *hcpu) { switch (action) { - case CPU_IDLE: + case S390_CPU_IDLE: stop_cpu_timer(); break; - case CPU_NOT_IDLE: + case S390_CPU_NOT_IDLE: start_cpu_timer(); break; } diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index 59aea65ce99..52084436ab6 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -4,7 +4,7 @@ EXTRA_AFLAGS := -traditional -lib-y += delay.o string.o uaccess_std.o uaccess_pt.o qrnnd.o -obj-$(CONFIG_32BIT) += div64.o +lib-y += delay.o string.o uaccess_std.o uaccess_pt.o +obj-$(CONFIG_32BIT) += div64.o qrnnd.o lib-$(CONFIG_64BIT) += uaccess_mvcos.o lib-$(CONFIG_SMP) += spinlock.o diff --git a/block/Kconfig b/block/Kconfig index a50f4811164..285935134bc 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -1,7 +1,7 @@ # # Block layer core configuration # -config BLOCK +menuconfig BLOCK bool "Enable the block layer" if EMBEDDED default y help @@ -49,6 +49,6 @@ config LSF If unsure, say Y. -endif +endif # BLOCK source block/Kconfig.iosched diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index baef5fc7cff..e0aa4dad674 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -92,6 +92,8 @@ struct cfq_data { struct cfq_queue *active_queue; struct cfq_io_context *active_cic; + struct cfq_queue *async_cfqq[IOPRIO_BE_NR]; + struct timer_list idle_class_timer; sector_t last_position; @@ -1351,8 +1353,8 @@ static void cfq_ioc_set_ioprio(struct io_context *ioc) } static struct cfq_queue * -cfq_get_queue(struct cfq_data *cfqd, int is_sync, struct task_struct *tsk, - gfp_t gfp_mask) +cfq_find_alloc_queue(struct cfq_data *cfqd, int is_sync, + struct task_struct *tsk, gfp_t gfp_mask) { struct cfq_queue *cfqq, *new_cfqq = NULL; struct cfq_io_context *cic; @@ -1405,12 +1407,35 @@ retry: if (new_cfqq) kmem_cache_free(cfq_pool, new_cfqq); - atomic_inc(&cfqq->ref); out: WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq); return cfqq; } +static struct cfq_queue * +cfq_get_queue(struct cfq_data *cfqd, int is_sync, struct task_struct *tsk, + gfp_t gfp_mask) +{ + const int ioprio = task_ioprio(tsk); + struct cfq_queue *cfqq = NULL; + + if (!is_sync) + cfqq = cfqd->async_cfqq[ioprio]; + if (!cfqq) + cfqq = cfq_find_alloc_queue(cfqd, is_sync, tsk, gfp_mask); + + /* + * pin the queue now that it's allocated, scheduler exit will prune it + */ + if (!is_sync && !cfqd->async_cfqq[ioprio]) { + atomic_inc(&cfqq->ref); + cfqd->async_cfqq[ioprio] = cfqq; + } + + atomic_inc(&cfqq->ref); + return cfqq; +} + /* * We drop cfq io contexts lazily, so we may find a dead one. */ @@ -2019,6 +2044,7 @@ static void cfq_exit_queue(elevator_t *e) { struct cfq_data *cfqd = e->elevator_data; request_queue_t *q = cfqd->queue; + int i; cfq_shutdown_timer_wq(cfqd); @@ -2035,6 +2061,13 @@ static void cfq_exit_queue(elevator_t *e) __cfq_exit_single_io_context(cfqd, cic); } + /* + * Put the async queues + */ + for (i = 0; i < IOPRIO_BE_NR; i++) + if (cfqd->async_cfqq[i]) + cfq_put_queue(cfqd->async_cfqq[i]); + spin_unlock_irq(q->queue_lock); cfq_shutdown_timer_wq(cfqd); diff --git a/block/elevator.c b/block/elevator.c index ce866eb75f6..4769a25d703 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -112,12 +112,8 @@ static inline int elv_try_merge(struct request *__rq, struct bio *bio) static struct elevator_type *elevator_find(const char *name) { struct elevator_type *e; - struct list_head *entry; - - list_for_each(entry, &elv_list) { - - e = list_entry(entry, struct elevator_type, list); + list_for_each_entry(e, &elv_list, list) { if (!strcmp(e->elevator_name, name)) return e; } @@ -1116,14 +1112,11 @@ ssize_t elv_iosched_show(request_queue_t *q, char *name) { elevator_t *e = q->elevator; struct elevator_type *elv = e->elevator_type; - struct list_head *entry; + struct elevator_type *__e; int len = 0; spin_lock(&elv_list_lock); - list_for_each(entry, &elv_list) { - struct elevator_type *__e; - - __e = list_entry(entry, struct elevator_type, list); + list_for_each_entry(__e, &elv_list, list) { if (!strcmp(elv->elevator_name, __e->elevator_name)) len += sprintf(name+len, "[%s] ", elv->elevator_name); else diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index c99b4635485..ef42bb2b12b 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -527,8 +527,6 @@ int blk_do_ordered(request_queue_t *q, struct request **rqp) static int flush_dry_bio_endio(struct bio *bio, unsigned int bytes, int error) { request_queue_t *q = bio->bi_private; - struct bio_vec *bvec; - int i; /* * This is dry run, restore bio_sector and size. We'll finish @@ -540,13 +538,6 @@ static int flush_dry_bio_endio(struct bio *bio, unsigned int bytes, int error) if (bio->bi_size) return 1; - /* Rewind bvec's */ - bio->bi_idx = 0; - bio_for_each_segment(bvec, bio, i) { - bvec->bv_len += bvec->bv_offset; - bvec->bv_offset = 0; - } - /* Reset bio */ set_bit(BIO_UPTODATE, &bio->bi_flags); bio->bi_size = q->bi_size; @@ -1304,9 +1295,9 @@ static int blk_hw_contig_segment(request_queue_t *q, struct bio *bio, if (unlikely(!bio_flagged(nxt, BIO_SEG_VALID))) blk_recount_segments(q, nxt); if (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)) || - BIOVEC_VIRT_OVERSIZE(bio->bi_hw_front_size + bio->bi_hw_back_size)) + BIOVEC_VIRT_OVERSIZE(bio->bi_hw_back_size + nxt->bi_hw_front_size)) return 0; - if (bio->bi_size + nxt->bi_size > q->max_segment_size) + if (bio->bi_hw_back_size + nxt->bi_hw_front_size > q->max_segment_size) return 0; return 1; diff --git a/drivers/Kconfig b/drivers/Kconfig index 050323fd79e..7916f4b86d2 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -24,8 +24,6 @@ source "drivers/scsi/Kconfig" source "drivers/ata/Kconfig" -source "drivers/cdrom/Kconfig" - source "drivers/md/Kconfig" source "drivers/message/fusion/Kconfig" @@ -54,6 +52,8 @@ source "drivers/spi/Kconfig" source "drivers/w1/Kconfig" +source "drivers/power/Kconfig" + source "drivers/hwmon/Kconfig" source "drivers/mfd/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index adad2f3d438..503d8256944 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_I2O) += message/ obj-$(CONFIG_RTC_LIB) += rtc/ obj-y += i2c/ obj-$(CONFIG_W1) += w1/ +obj-$(CONFIG_POWER_SUPPLY) += power/ obj-$(CONFIG_HWMON) += hwmon/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ diff --git a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c index 674bf81c6e6..423ed08fb6f 100644 --- a/drivers/acorn/block/fd1772.c +++ b/drivers/acorn/block/fd1772.c @@ -1246,7 +1246,7 @@ repeat: del_timer(&motor_off_timer); ReqCnt = 0; - ReqCmd = CURRENT->cmd; + ReqCmd = rq_data_dir(CURRENT); ReqBlock = CURRENT->sector; ReqBuffer = CURRENT->buffer; setup_req_params(drive); diff --git a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c index 689a4c3542b..d85520f78e6 100644 --- a/drivers/acorn/block/mfmhd.c +++ b/drivers/acorn/block/mfmhd.c @@ -439,7 +439,7 @@ static void mfm_rw_intr(void) a choice of command end or some data which is ready to be collected */ /* I think we have to transfer data while the interrupt line is on and its not any other type of interrupt */ - if (CURRENT->cmd == WRITE) { + if (rq_data_dir(CURRENT) == WRITE) { extern void hdc63463_writedma(void); if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n"); @@ -799,7 +799,7 @@ static void issue_request(unsigned int block, unsigned int nsect, raw_cmd.head = start_head; raw_cmd.cylinder = track / p->heads; raw_cmd.cmdtype = CURRENT->cmd; - raw_cmd.cmdcode = CURRENT->cmd == WRITE ? CMD_WD : CMD_RD; + raw_cmd.cmdcode = rq_data_dir(CURRENT) == WRITE ? CMD_WD : CMD_RD; raw_cmd.cmddata[0] = dev + 1; /* DAG: +1 to get US */ raw_cmd.cmddata[1] = raw_cmd.head; raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8; @@ -830,7 +830,7 @@ static void issue_request(unsigned int block, unsigned int nsect, hdc63463_dataleft = nsect * 256; /* Better way? */ DBG("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n", - raw_cmd.dev + 'a', (CURRENT->cmd == READ) ? "read" : "writ", + raw_cmd.dev + 'a', rq_data_dir(CURRENT) == READ ? "read" : "writ", raw_cmd.cylinder, raw_cmd.head, raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT); @@ -917,13 +917,6 @@ static void mfm_request(void) DBG("mfm_request: block after offset=%d\n", block); - if (CURRENT->cmd != READ && CURRENT->cmd != WRITE) { - printk("unknown mfm-command %d\n", CURRENT->cmd); - end_request(CURRENT, 0); - Busy = 0; - printk("mfm: continue 4\n"); - continue; - } issue_request(block, nsect, CURRENT); break; diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index b4c8319138b..6e23af1ecbd 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -2,9 +2,12 @@ # Block device driver configuration # -if BLOCK +menuconfig BLK_DEV + bool "Block devices" + depends on BLOCK + default y -menu "Block devices" +if BLK_DEV config BLK_DEV_FD tristate "Normal floppy disk support" @@ -56,40 +59,9 @@ config AMIGA_Z2RAM To compile this driver as a module, choose M here: the module will be called z2ram. -config ATARI_ACSI - tristate "Atari ACSI support" - depends on ATARI && BROKEN - ---help--- - This enables support for the Atari ACSI interface. The driver - supports hard disks and CD-ROMs, which have 512-byte sectors, or can - be switched to that mode. Due to the ACSI command format, only disks - up to 1 GB are supported. Special support for certain ACSI to SCSI - adapters, which could relax that, isn't included yet. The ACSI - driver is also the basis for certain other drivers for devices - attached to the ACSI bus: Atari SLM laser printer, BioNet-100 - Ethernet, and PAMsNet Ethernet. If you want to use one of these - devices, you need ACSI support, too. - - To compile this driver as a module, choose M here: the - module will be called acsi. - -comment "Some devices (e.g. CD jukebox) support multiple LUNs" - depends on ATARI && ATARI_ACSI - -config ACSI_MULTI_LUN - bool "Probe all LUNs on each ACSI device" - depends on ATARI_ACSI - help - If you have a ACSI device that supports more than one LUN (Logical - Unit Number), e.g. a CD jukebox, you should say Y here so that all - will be found by the ACSI driver. An ACSI device with multiple LUNs - acts logically like multiple ACSI devices. The vast majority of ACSI - devices have only one LUN, and so most people can say N here and - should in fact do so, because it is safer. - config ATARI_SLM tristate "Atari SLM laser printer support" - depends on ATARI && ATARI_ACSI!=n + depends on ATARI help If you have an Atari SLM laser printer, say Y to include support for it in the kernel. Otherwise, say N. This driver is also available as @@ -453,6 +425,4 @@ config ATA_OVER_ETH source "drivers/s390/block/Kconfig" -endmenu - -endif +endif # BLK_DEV diff --git a/drivers/block/Makefile b/drivers/block/Makefile index dd88e33c1eb..e5f98acc5d5 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_MAC_FLOPPY) += swim3.o obj-$(CONFIG_BLK_DEV_FD) += floppy.o obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o -obj-$(CONFIG_ATARI_ACSI) += acsi.o obj-$(CONFIG_ATARI_SLM) += acsi_slm.o obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o obj-$(CONFIG_BLK_DEV_RAM) += rd.o diff --git a/drivers/block/acsi.c b/drivers/block/acsi.c deleted file mode 100644 index e3d9152e231..00000000000 --- a/drivers/block/acsi.c +++ /dev/null @@ -1,1825 +0,0 @@ -/* - * acsi.c -- Device driver for Atari ACSI hard disks - * - * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> - * - * Some parts are based on hd.c by Linus Torvalds - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - * - */ - -/* - * Still to in this file: - * - If a command ends with an error status (!= 0), the following - * REQUEST SENSE commands (4 to fill the ST-DMA FIFO) are done by - * polling the _IRQ signal (not interrupt-driven). This should be - * avoided in future because it takes up a non-neglectible time in - * the interrupt service routine while interrupts are disabled. - * Maybe a timer interrupt will get lost :-( - */ - -/* - * General notes: - * - * - All ACSI devices (disks, CD-ROMs, ...) use major number 28. - * Minors are organized like it is with SCSI: The upper 4 bits - * identify the device, the lower 4 bits the partition. - * The device numbers (the upper 4 bits) are given in the same - * order as the devices are found on the bus. - * - Up to 8 LUNs are supported for each target (if CONFIG_ACSI_MULTI_LUN - * is defined), but only a total of 16 devices (due to minor - * numbers...). Note that Atari allows only a maximum of 4 targets - * (i.e. controllers, not devices) on the ACSI bus! - * - A optimizing scheme similar to SCSI scatter-gather is implemented. - * - Removable media are supported. After a medium change to device - * is reinitialized (partition check etc.). Also, if the device - * knows the PREVENT/ALLOW MEDIUM REMOVAL command, the door should - * be locked and unlocked when mounting the first or unmounting the - * last filesystem on the device. The code is untested, because I - * don't have a removable hard disk. - * - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/genhd.h> -#include <linux/delay.h> -#include <linux/mm.h> -#include <linux/major.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <scsi/scsi.h> /* for SCSI_IOCTL_GET_IDLUN */ -#include <scsi/scsi_ioctl.h> -#include <linux/hdreg.h> /* for HDIO_GETGEO */ -#include <linux/blkpg.h> -#include <linux/buffer_head.h> -#include <linux/blkdev.h> - -#include <asm/setup.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/uaccess.h> -#include <asm/atarihw.h> -#include <asm/atariints.h> -#include <asm/atari_acsi.h> -#include <asm/atari_stdma.h> -#include <asm/atari_stram.h> - -static void (*do_acsi)(void) = NULL; -static struct request_queue *acsi_queue; -#define QUEUE (acsi_queue) -#define CURRENT elv_next_request(acsi_queue) - -#define DEBUG -#undef DEBUG_DETECT -#undef NO_WRITE - -#define MAX_ERRORS 8 /* Max read/write errors/sector */ -#define MAX_LUN 8 /* Max LUNs per target */ -#define MAX_DEV 16 - -#define ACSI_BUFFER_SIZE (16*1024) /* "normal" ACSI buffer size */ -#define ACSI_BUFFER_MINSIZE (2048) /* min. buf size if ext. DMA */ -#define ACSI_BUFFER_SIZE_ORDER 2 /* order size for above */ -#define ACSI_BUFFER_MINSIZE_ORDER 0 /* order size for above */ -#define ACSI_BUFFER_SECTORS (ACSI_BUFFER_SIZE/512) - -#define ACSI_BUFFER_ORDER \ - (ATARIHW_PRESENT(EXTD_DMA) ? \ - ACSI_BUFFER_MINSIZE_ORDER : \ - ACSI_BUFFER_SIZE_ORDER) - -#define ACSI_TIMEOUT (4*HZ) - -/* minimum delay between two commands */ - -#define COMMAND_DELAY 500 - -typedef enum { - NONE, HARDDISK, CDROM -} ACSI_TYPE; - -struct acsi_info_struct { - ACSI_TYPE type; /* type of device */ - unsigned target; /* target number */ - unsigned lun; /* LUN in target controller */ - unsigned removable : 1; /* Flag for removable media */ - unsigned read_only : 1; /* Flag for read only devices */ - unsigned old_atari_disk : 1; /* Is an old Atari disk */ - unsigned changed : 1; /* Medium has been changed */ - unsigned long size; /* #blocks */ - int access_count; -} acsi_info[MAX_DEV]; - -/* - * SENSE KEYS - */ - -#define NO_SENSE 0x00 -#define RECOVERED_ERROR 0x01 -#define NOT_READY 0x02 -#define MEDIUM_ERROR 0x03 -#define HARDWARE_ERROR 0x04 -#define ILLEGAL_REQUEST 0x05 -#define UNIT_ATTENTION 0x06 -#define DATA_PROTECT 0x07 -#define BLANK_CHECK 0x08 -#define COPY_ABORTED 0x0a -#define ABORTED_COMMAND 0x0b -#define VOLUME_OVERFLOW 0x0d -#define MISCOMPARE 0x0e - - -/* - * DEVICE TYPES - */ - -#define TYPE_DISK 0x00 -#define TYPE_TAPE 0x01 -#define TYPE_WORM 0x04 -#define TYPE_ROM 0x05 -#define TYPE_MOD 0x07 -#define TYPE_NO_LUN 0x7f - -/* The data returned by MODE SENSE differ between the old Atari - * hard disks and SCSI disks connected to ACSI. In the following, both - * formats are defined and some macros to operate on them potably. - */ - -typedef struct { - unsigned long dummy[2]; - unsigned long sector_size; - unsigned char format_code; -#define ATARI_SENSE_FORMAT_FIX 1 -#define ATARI_SENSE_FORMAT_CHNG 2 - unsigned char cylinders_h; - unsigned char cylinders_l; - unsigned char heads; - unsigned char reduced_h; - unsigned char reduced_l; - unsigned char precomp_h; - unsigned char precomp_l; - unsigned char landing_zone; - unsigned char steprate; - unsigned char type; -#define ATARI_SENSE_TYPE_FIXCHNG_MASK 4 -#define ATARI_SENSE_TYPE_SOFTHARD_MASK 8 -#define ATARI_SENSE_TYPE_FIX 4 -#define ATARI_SENSE_TYPE_CHNG 0 -#define ATARI_SENSE_TYPE_SOFT 0 -#define ATARI_SENSE_TYPE_HARD 8 - unsigned char sectors; -} ATARI_SENSE_DATA; - -#define ATARI_CAPACITY(sd) \ - (((int)((sd).cylinders_h<<8)|(sd).cylinders_l) * \ - (sd).heads * (sd).sectors) - - -typedef struct { - unsigned char dummy1; - unsigned char medium_type; - unsigned char dummy2; - unsigned char descriptor_size; - unsigned long block_count; - unsigned long sector_size; - /* Page 0 data */ - unsigned char page_code; - unsigned char page_size; - unsigned char page_flags; - unsigned char qualifier; -} SCSI_SENSE_DATA; - -#define SCSI_CAPACITY(sd) ((sd).block_count & 0xffffff) - - -typedef union { - ATARI_SENSE_DATA atari; - SCSI_SENSE_DATA scsi; -} SENSE_DATA; - -#define SENSE_TYPE_UNKNOWN 0 -#define SENSE_TYPE_ATARI 1 -#define SENSE_TYPE_SCSI 2 - -#define SENSE_TYPE(sd) \ - (((sd).atari.dummy[0] == 8 && \ - ((sd).atari.format_code == 1 || \ - (sd).atari.format_code == 2)) ? SENSE_TYPE_ATARI : \ - ((sd).scsi.dummy1 >= 11) ? SENSE_TYPE_SCSI : \ - SENSE_TYPE_UNKNOWN) - -#define CAPACITY(sd) \ - (SENSE_TYPE(sd) == SENSE_TYPE_ATARI ? \ - ATARI_CAPACITY((sd).atari) : \ - SCSI_CAPACITY((sd).scsi)) - -#define SECTOR_SIZE(sd) \ - (SENSE_TYPE(sd) == SENSE_TYPE_ATARI ? \ - (sd).atari.sector_size : \ - (sd).scsi.sector_size & 0xffffff) - -/* Default size if capacity cannot be determined (1 GByte) */ -#define DEFAULT_SIZE 0x1fffff - -#define CARTRCH_STAT(aip,buf) \ - (aip->old_atari_disk ? \ - (((buf)[0] & 0x7f) == 0x28) : \ - ((((buf)[0] & 0x70) == 0x70) ? \ - (((buf)[2] & 0x0f) == 0x06) : \ - (((buf)[0] & 0x0f) == 0x06))) \ - -/* These two are also exported to other drivers that work on the ACSI bus and - * need an ST-RAM buffer. */ -char *acsi_buffer; -unsigned long phys_acsi_buffer; - -static int NDevices; - -static int CurrentNReq; -static int CurrentNSect; -static char *CurrentBuffer; - -static DEFINE_SPINLOCK(acsi_lock); - - -#define SET_TIMER() mod_timer(&acsi_timer, jiffies + ACSI_TIMEOUT) -#define CLEAR_TIMER() del_timer(&acsi_timer) - -static unsigned long STramMask; -#define STRAM_ADDR(a) (((a) & STramMask) == 0) - - - -/* ACSI commands */ - -static char tur_cmd[6] = { 0x00, 0, 0, 0, 0, 0 }; -static char modesense_cmd[6] = { 0x1a, 0, 0, 0, 24, 0 }; -static char modeselect_cmd[6] = { 0x15, 0, 0, 0, 12, 0 }; -static char inquiry_cmd[6] = { 0x12, 0, 0, 0,255, 0 }; -static char reqsense_cmd[6] = { 0x03, 0, 0, 0, 4, 0 }; -static char read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 }; -static char write_cmd[6] = { 0x0a, 0, 0, 0, 0, 0 }; -static char pa_med_rem_cmd[6] = { 0x1e, 0, 0, 0, 0, 0 }; - -#define CMDSET_TARG_LUN(cmd,targ,lun) \ - do { \ - cmd[0] = (cmd[0] & ~0xe0) | (targ)<<5; \ - cmd[1] = (cmd[1] & ~0xe0) | (lun)<<5; \ - } while(0) - -#define CMDSET_BLOCK(cmd,blk) \ - do { \ - unsigned long __blk = (blk); \ - cmd[3] = __blk; __blk >>= 8; \ - cmd[2] = __blk; __blk >>= 8; \ - cmd[1] = (cmd[1] & 0xe0) | (__blk & 0x1f); \ - } while(0) - -#define CMDSET_LEN(cmd,len) \ - do { \ - cmd[4] = (len); \ - } while(0) - -/* ACSI errors (from REQUEST SENSE); There are two tables, one for the - * old Atari disks and one for SCSI on ACSI disks. - */ - -struct acsi_error { - unsigned char code; - const char *text; -} atari_acsi_errors[] = { - { 0x00, "No error (??)" }, - { 0x01, "No index pulses" }, - { 0x02, "Seek not complete" }, - { 0x03, "Write fault" }, - { 0x04, "Drive not ready" }, - { 0x06, "No Track 00 signal" }, - { 0x10, "ECC error in ID field" }, - { 0x11, "Uncorrectable data error" }, - { 0x12, "ID field address mark not found" }, - { 0x13, "Data field address mark not found" }, - { 0x14, "Record not found" }, - { 0x15, "Seek error" }, - { 0x18, "Data check in no retry mode" }, - { 0x19, "ECC error during verify" }, - { 0x1a, "Access to bad block" }, - { 0x1c, "Unformatted or bad format" }, - { 0x20, "Invalid command" }, - { 0x21, "Invalid block address" }, - { 0x23, "Volume overflow" }, - { 0x24, "Invalid argument" }, - { 0x25, "Invalid drive number" }, - { 0x26, "Byte zero parity check" }, - { 0x28, "Cartride changed" }, - { 0x2c, "Error count overflow" }, - { 0x30, "Controller selftest failed" } -}, - - scsi_acsi_errors[] = { - { 0x00, "No error (??)" }, - { 0x01, "Recovered error" }, - { 0x02, "Drive not ready" }, - { 0x03, "Uncorrectable medium error" }, - { 0x04, "Hardware error" }, - { 0x05, "Illegal request" }, - { 0x06, "Unit attention (Reset or cartridge changed)" }, - { 0x07, "Data protection" }, - { 0x08, "Blank check" }, - { 0x0b, "Aborted Command" }, - { 0x0d, "Volume overflow" } -}; - - - -/***************************** Prototypes *****************************/ - -static int acsicmd_dma( const char *cmd, char *buffer, int blocks, int - rwflag, int enable); -static int acsi_reqsense( char *buffer, int targ, int lun); -static void acsi_print_error(const unsigned char *errblk, struct acsi_info_struct *aip); -static irqreturn_t acsi_interrupt (int irq, void *data); -static void unexpected_acsi_interrupt( void ); -static void bad_rw_intr( void ); -static void read_intr( void ); -static void write_intr( void); -static void acsi_times_out( unsigned long dummy ); -static void copy_to_acsibuffer( void ); -static void copy_from_acsibuffer( void ); -static void do_end_requests( void ); -static void do_acsi_request( request_queue_t * ); -static void redo_acsi_request( void ); -static int acsi_ioctl( struct inode *inode, struct file *file, unsigned int - cmd, unsigned long arg ); -static int acsi_open( struct inode * inode, struct file * filp ); -static int acsi_release( struct inode * inode, struct file * file ); -static void acsi_prevent_removal(struct acsi_info_struct *aip, int flag ); -static int acsi_change_blk_size( int target, int lun); -static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd ); -static int acsi_revalidate (struct gendisk *disk); - -/************************* End of Prototypes **************************/ - - -DEFINE_TIMER(acsi_timer, acsi_times_out, 0, 0); - - -#ifdef CONFIG_ATARI_SLM - -extern int attach_slm( int target, int lun ); -extern int slm_init( void ); - -#endif - - - -/*********************************************************************** - * - * ACSI primitives - * - **********************************************************************/ - - -/* - * The following two functions wait for _IRQ to become Low or High, - * resp., with a timeout. The 'timeout' parameter is in jiffies - * (10ms). - * If the functions are called with timer interrupts on (int level < - * 6), the timeout is based on the 'jiffies' variable to provide exact - * timeouts for device probing etc. - * If interrupts are disabled, the number of tries is based on the - * 'loops_per_jiffy' variable. A rough estimation is sufficient here... - */ - -#define INT_LEVEL \ - ({ unsigned __sr; \ - __asm__ __volatile__ ( "movew %/sr,%0" : "=dm" (__sr) ); \ - (__sr >> 8) & 7; \ - }) - -int acsi_wait_for_IRQ( unsigned timeout ) - -{ - if (INT_LEVEL < 6) { - unsigned long maxjif = jiffies + timeout; - while (time_before(jiffies, maxjif)) - if (!(mfp.par_dt_reg & 0x20)) return( 1 ); - } - else { - long tries = loops_per_jiffy / 8 * timeout; - while( --tries >= 0 ) - if (!(mfp.par_dt_reg & 0x20)) return( 1 ); - } - return( 0 ); /* timeout! */ -} - - -int acsi_wait_for_noIRQ( unsigned timeout ) - -{ - if (INT_LEVEL < 6) { - unsigned long maxjif = jiffies + timeout; - while (time_before(jiffies, maxjif)) - if (mfp.par_dt_reg & 0x20) return( 1 ); - } - else { - long tries = loops_per_jiffy * timeout / 8; - while( tries-- >= 0 ) - if (mfp.par_dt_reg & 0x20) return( 1 ); - } - return( 0 ); /* timeout! */ -} - -static struct timeval start_time; - -void -acsi_delay_start(void) -{ - do_gettimeofday(&start_time); -} - -/* wait from acsi_delay_start to now usec (<1E6) usec */ - -void -acsi_delay_end(long usec) -{ - struct timeval end_time; - long deltau,deltas; - do_gettimeofday(&end_time); - deltau=end_time.tv_usec - start_time.tv_usec; - deltas=end_time.tv_sec - start_time.tv_sec; - if (deltas > 1 || deltas < 0) - return; - if (deltas > 0) - deltau += 1000*1000; - if (deltau >= usec) - return; - udelay(usec-deltau); -} - -/* acsicmd_dma() sends an ACSI command and sets up the DMA to transfer - * 'blocks' blocks of 512 bytes from/to 'buffer'. - * Because the _IRQ signal is used for handshaking the command bytes, - * the ACSI interrupt has to be disabled in this function. If the end - * of the operation should be signalled by a real interrupt, it has to be - * reenabled afterwards. - */ - -static int acsicmd_dma( const char *cmd, char *buffer, int blocks, int rwflag, int enable) - -{ unsigned long flags, paddr; - int i; - -#ifdef NO_WRITE - if (rwflag || *cmd == 0x0a) { - printk( "ACSI: Write commands disabled!\n" ); - return( 0 ); - } -#endif - - rwflag = rwflag ? 0x100 : 0; - paddr = virt_to_phys( buffer ); - - acsi_delay_end(COMMAND_DELAY); - DISABLE_IRQ(); - - local_irq_save(flags); - /* Low on A1 */ - dma_wd.dma_mode_status = 0x88 | rwflag; - MFPDELAY(); - - /* set DMA address */ - dma_wd.dma_lo = (unsigned char)paddr; - paddr >>= 8; - MFPDELAY(); - dma_wd.dma_md = (unsigned char)paddr; - paddr >>= 8; - MFPDELAY(); - if (ATARIHW_PRESENT(EXTD_DMA)) - st_dma_ext_dmahi = (unsigned short)paddr; - else - dma_wd.dma_hi = (unsigned char)paddr; - MFPDELAY(); - local_irq_restore(flags); - - /* send the command bytes except the last */ - for( i = 0; i < 5; ++i ) { - DMA_LONG_WRITE( *cmd++, 0x8a | rwflag ); - udelay(20); - if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */ - } - - /* Clear FIFO and switch DMA to correct direction */ - dma_wd.dma_mode_status = 0x92 | (rwflag ^ 0x100); - MFPDELAY(); - dma_wd.dma_mode_status = 0x92 | rwflag; - MFPDELAY(); - - /* How many sectors for DMA */ - dma_wd.fdc_acces_seccount = blocks; - MFPDELAY(); - - /* send last command byte */ - dma_wd.dma_mode_status = 0x8a | rwflag; - MFPDELAY(); - DMA_LONG_WRITE( *cmd++, 0x0a | rwflag ); - if (enable) - ENABLE_IRQ(); - udelay(80); - - return( 1 ); -} - - -/* - * acsicmd_nodma() sends an ACSI command that requires no DMA. - */ - -int acsicmd_nodma( const char *cmd, int enable) - -{ int i; - - acsi_delay_end(COMMAND_DELAY); - DISABLE_IRQ(); - - /* send first command byte */ - dma_wd.dma_mode_status = 0x88; - MFPDELAY(); - DMA_LONG_WRITE( *cmd++, 0x8a ); - udelay(20); - if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */ - - /* send the intermediate command bytes */ - for( i = 0; i < 4; ++i ) { - DMA_LONG_WRITE( *cmd++, 0x8a ); - udelay(20); - if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */ - } - - /* send last command byte */ - DMA_LONG_WRITE( *cmd++, 0x0a ); - if (enable) - ENABLE_IRQ(); - udelay(80); - - return( 1 ); - /* Note that the ACSI interrupt is still disabled after this - * function. If you want to get the IRQ delivered, enable it manually! - */ -} - - -static int acsi_reqsense( char *buffer, int targ, int lun) - -{ - CMDSET_TARG_LUN( reqsense_cmd, targ, lun); - if (!acsicmd_dma( reqsense_cmd, buffer, 1, 0, 0 )) return( 0 ); - if (!acsi_wait_for_IRQ( 10 )) return( 0 ); - acsi_getstatus(); - if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 ); - if (!acsi_wait_for_IRQ( 10 )) return( 0 ); - acsi_getstatus(); - if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 ); - if (!acsi_wait_for_IRQ( 10 )) return( 0 ); - acsi_getstatus(); - if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 ); - if (!acsi_wait_for_IRQ( 10 )) return( 0 ); - acsi_getstatus(); - dma_cache_maintenance( virt_to_phys(buffer), 16, 0 ); - - return( 1 ); -} - - -/* - * ACSI status phase: get the status byte from the bus - * - * I've seen several times that a 0xff status is read, propably due to - * a timing error. In this case, the procedure is repeated after the - * next _IRQ edge. - */ - -int acsi_getstatus( void ) - -{ int status; - - DISABLE_IRQ(); - for(;;) { - if (!acsi_wait_for_IRQ( 100 )) { - acsi_delay_start(); - return( -1 ); - } - dma_wd.dma_mode_status = 0x8a; - MFPDELAY(); - status = dma_wd.fdc_acces_seccount; - if (status != 0xff) break; -#ifdef DEBUG - printk("ACSI: skipping 0xff status byte\n" ); -#endif - udelay(40); - acsi_wait_for_noIRQ( 20 ); - } - dma_wd.dma_mode_status = 0x80; - udelay(40); - acsi_wait_for_noIRQ( 20 ); - - acsi_delay_start(); - return( status & 0x1f ); /* mask of the device# */ -} - - -#if (defined(CONFIG_ATARI_SLM) || defined(CONFIG_ATARI_SLM_MODULE)) - -/* Receive data in an extended status phase. Needed by SLM printer. */ - -int acsi_extstatus( char *buffer, int cnt ) - -{ int status; - - DISABLE_IRQ(); - udelay(80); - while( cnt-- > 0 ) { - if (!acsi_wait_for_IRQ( 40 )) return( 0 ); - dma_wd.dma_mode_status = 0x8a; - MFPDELAY(); - status = dma_wd.fdc_acces_seccount; - MFPDELAY(); - *buffer++ = status & 0xff; - udelay(40); - } - return( 1 ); -} - - -/* Finish an extended status phase */ - -void acsi_end_extstatus( void ) - -{ - dma_wd.dma_mode_status = 0x80; - udelay(40); - acsi_wait_for_noIRQ( 20 ); - acsi_delay_start(); -} - - -/* Send data in an extended command phase */ - -int acsi_extcmd( unsigned char *buffer, int cnt ) - -{ - while( cnt-- > 0 ) { - DMA_LONG_WRITE( *buffer++, 0x8a ); - udelay(20); - if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */ - } - return( 1 ); -} - -#endif - - -static void acsi_print_error(const unsigned char *errblk, struct acsi_info_struct *aip) - -{ int atari_err, i, errcode; - struct acsi_error *arr; - - atari_err = aip->old_atari_disk; - if (atari_err) - errcode = errblk[0] & 0x7f; - else - if ((errblk[0] & 0x70) == 0x70) - errcode = errblk[2] & 0x0f; - else - errcode = errblk[0] & 0x0f; - - printk( KERN_ERR "ACSI error 0x%02x", errcode ); - - if (errblk[0] & 0x80) - printk( " for sector %d", - ((errblk[1] & 0x1f) << 16) | - (errblk[2] << 8) | errblk[0] ); - - arr = atari_err ? atari_acsi_errors : scsi_acsi_errors; - i = atari_err ? sizeof(atari_acsi_errors)/sizeof(*atari_acsi_errors) : - sizeof(scsi_acsi_errors)/sizeof(*scsi_acsi_errors); - - for( --i; i >= 0; --i ) - if (arr[i].code == errcode) break; - if (i >= 0) - printk( ": %s\n", arr[i].text ); -} - -/******************************************************************* - * - * ACSI interrupt routine - * Test, if this is a ACSI interrupt and call the irq handler - * Otherwise ignore this interrupt. - * - *******************************************************************/ - -static irqreturn_t acsi_interrupt(int irq, void *data ) - -{ void (*acsi_irq_handler)(void) = do_acsi; - - do_acsi = NULL; - CLEAR_TIMER(); - - if (!acsi_irq_handler) - acsi_irq_handler = unexpected_acsi_interrupt; - acsi_irq_handler(); - return IRQ_HANDLED; -} - - -/****************************************************************** - * - * The Interrupt handlers - * - *******************************************************************/ - - -static void unexpected_acsi_interrupt( void ) - -{ - printk( KERN_WARNING "Unexpected ACSI interrupt\n" ); -} - - -/* This function is called in case of errors. Because we cannot reset - * the ACSI bus or a single device, there is no other choice than - * retrying several times :-( - */ - -static void bad_rw_intr( void ) - -{ - if (!CURRENT) - return; - - if (++CURRENT->errors >= MAX_ERRORS) - end_request(CURRENT, 0); - /* Otherwise just retry */ -} - - -static void read_intr( void ) - -{ int status; - - status = acsi_getstatus(); - if (status != 0) { - struct gendisk *disk = CURRENT->rq_disk; - struct acsi_info_struct *aip = disk->private_data; - printk(KERN_ERR "%s: ", disk->disk_name); - if (!acsi_reqsense(acsi_buffer, aip->target, aip->lun)) - printk( "ACSI error and REQUEST SENSE failed (status=0x%02x)\n", status ); - else { - acsi_print_error(acsi_buffer, aip); - if (CARTRCH_STAT(aip, acsi_buffer)) - aip->changed = 1; - } - ENABLE_IRQ(); - bad_rw_intr(); - redo_acsi_request(); - return; - } - - dma_cache_maintenance( virt_to_phys(CurrentBuffer), CurrentNSect*512, 0 ); - if (CurrentBuffer == acsi_buffer) - copy_from_acsibuffer(); - - do_end_requests(); - redo_acsi_request(); -} - - -static void write_intr(void) - -{ int status; - - status = acsi_getstatus(); - if (status != 0) { - struct gendisk *disk = CURRENT->rq_disk; - struct acsi_info_struct *aip = disk->private_data; - printk( KERN_ERR "%s: ", disk->disk_name); - if (!acsi_reqsense( acsi_buffer, aip->target, aip->lun)) - printk( "ACSI error and REQUEST SENSE failed (status=0x%02x)\n", status ); - else { - acsi_print_error(acsi_buffer, aip); - if (CARTRCH_STAT(aip, acsi_buffer)) - aip->changed = 1; - } - bad_rw_intr(); - redo_acsi_request(); - return; - } - - do_end_requests(); - redo_acsi_request(); -} - - -static void acsi_times_out( unsigned long dummy ) - -{ - DISABLE_IRQ(); - if (!do_acsi) return; - - do_acsi = NULL; - printk( KERN_ERR "ACSI timeout\n" ); - if (!CURRENT) - return; - if (++CURRENT->errors >= MAX_ERRORS) { -#ifdef DEBUG - printk( KERN_ERR "ACSI: too many errors.\n" ); -#endif - end_request(CURRENT, 0); - } - - redo_acsi_request(); -} - - - -/*********************************************************************** - * - * Scatter-gather utility functions - * - ***********************************************************************/ - - -static void copy_to_acsibuffer( void ) - -{ int i; - char *src, *dst; - struct buffer_head *bh; - - src = CURRENT->buffer; - dst = acsi_buffer; - bh = CURRENT->bh; - - if (!bh) - memcpy( dst, src, CurrentNSect*512 ); - else - for( i = 0; i < CurrentNReq; ++i ) { - memcpy( dst, src, bh->b_size ); - dst += bh->b_size; - if ((bh = bh->b_reqnext)) - src = bh->b_data; - } -} - - -static void copy_from_acsibuffer( void ) - -{ int i; - char *src, *dst; - struct buffer_head *bh; - - dst = CURRENT->buffer; - src = acsi_buffer; - bh = CURRENT->bh; - - if (!bh) - memcpy( dst, src, CurrentNSect*512 ); - else - for( i = 0; i < CurrentNReq; ++i ) { - memcpy( dst, src, bh->b_size ); - src += bh->b_size; - if ((bh = bh->b_reqnext)) - dst = bh->b_data; - } -} - - -static void do_end_requests( void ) - -{ int i, n; - - if (!CURRENT->bh) { - CURRENT->nr_sectors -= CurrentNSect; - CURRENT->current_nr_sectors -= CurrentNSect; - CURRENT->sector += CurrentNSect; - if (CURRENT->nr_sectors == 0) - end_request(CURRENT, 1); - } - else { - for( i = 0; i < CurrentNReq; ++i ) { - n = CURRENT->bh->b_size >> 9; - CURRENT->nr_sectors -= n; - CURRENT->current_nr_sectors -= n; - CURRENT->sector += n; - end_request(CURRENT, 1); - } - } -} - - - - -/*********************************************************************** - * - * do_acsi_request and friends - * - ***********************************************************************/ - -static void do_acsi_request( request_queue_t * q ) - -{ - stdma_lock( acsi_interrupt, NULL ); - redo_acsi_request(); -} - - -static void redo_acsi_request( void ) -{ - unsigned block, target, lun, nsect; - char *buffer; - unsigned long pbuffer; - struct buffer_head *bh; - struct gendisk *disk; - struct acsi_info_struct *aip; - - repeat: - CLEAR_TIMER(); - - if (do_acsi) - return; - - if (!CURRENT) { - do_acsi = NULL; - ENABLE_IRQ(); - stdma_release(); - return; - } - - disk = CURRENT->rq_disk; - aip = disk->private_data; - if (CURRENT->bh) { - if (!CURRENT->bh && !buffer_locked(CURRENT->bh)) - panic("ACSI: block not locked"); - } - - block = CURRENT->sector; - if (block+CURRENT->nr_sectors >= get_capacity(disk)) { -#ifdef DEBUG - printk( "%s: attempted access for blocks %d...%ld past end of device at block %ld.\n", - disk->disk_name, - block, block + CURRENT->nr_sectors - 1, - get_capacity(disk)); -#endif - end_request(CURRENT, 0); - goto repeat; - } - if (aip->changed) { - printk( KERN_NOTICE "%s: request denied because cartridge has " - "been changed.\n", disk->disk_name); - end_request(CURRENT, 0); - goto repeat; - } - - target = aip->target; - lun = aip->lun; - - /* Find out how many sectors should be transferred from/to - * consecutive buffers and thus can be done with a single command. - */ - buffer = CURRENT->buffer; - pbuffer = virt_to_phys(buffer); - nsect = CURRENT->current_nr_sectors; - CurrentNReq = 1; - - if ((bh = CURRENT->bh) && bh != CURRENT->bhtail) { - if (!STRAM_ADDR(pbuffer)) { - /* If transfer is done via the ACSI buffer anyway, we can - * assemble as much bh's as fit in the buffer. - */ - while( (bh = bh->b_reqnext) ) { - if (nsect + (bh->b_size>>9) > ACSI_BUFFER_SECTORS) break; - nsect += bh->b_size >> 9; - ++CurrentNReq; - if (bh == CURRENT->bhtail) break; - } - buffer = acsi_buffer; - pbuffer = phys_acsi_buffer; - } - else { - unsigned long pendadr, pnewadr; - pendadr = pbuffer + nsect*512; - while( (bh = bh->b_reqnext) ) { - pnewadr = virt_to_phys(bh->b_data); - if (!STRAM_ADDR(pnewadr) || pendadr != pnewadr) break; - nsect += bh->b_size >> 9; - pendadr = pnewadr + bh->b_size; - ++CurrentNReq; - if (bh == CURRENT->bhtail) break; - } - } - } - else { - if (!STRAM_ADDR(pbuffer)) { - buffer = acsi_buffer; - pbuffer = phys_acsi_buffer; - if (nsect > ACSI_BUFFER_SECTORS) - nsect = ACSI_BUFFER_SECTORS; - } - } - CurrentBuffer = buffer; - CurrentNSect = nsect; - - if (rq_data_dir(CURRENT) == WRITE) { - CMDSET_TARG_LUN( write_cmd, target, lun ); - CMDSET_BLOCK( write_cmd, block ); - CMDSET_LEN( write_cmd, nsect ); - if (buffer == acsi_buffer) - copy_to_acsibuffer(); - dma_cache_maintenance( pbuffer, nsect*512, 1 ); - do_acsi = write_intr; - if (!acsicmd_dma( write_cmd, buffer, nsect, 1, 1)) { - do_acsi = NULL; - printk( KERN_ERR "ACSI (write): Timeout in command block\n" ); - bad_rw_intr(); - goto repeat; - } - SET_TIMER(); - return; - } - if (rq_data_dir(CURRENT) == READ) { - CMDSET_TARG_LUN( read_cmd, target, lun ); - CMDSET_BLOCK( read_cmd, block ); - CMDSET_LEN( read_cmd, nsect ); - do_acsi = read_intr; - if (!acsicmd_dma( read_cmd, buffer, nsect, 0, 1)) { - do_acsi = NULL; - printk( KERN_ERR "ACSI (read): Timeout in command block\n" ); - bad_rw_intr(); - goto repeat; - } - SET_TIMER(); - return; - } - panic("unknown ACSI command"); -} - - - -/*********************************************************************** - * - * Misc functions: ioctl, open, release, check_change, ... - * - ***********************************************************************/ - -static int acsi_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - struct acsi_info_struct *aip = bdev->bd_disk->private_data; - - /* - * Just fake some geometry here, it's nonsense anyway - * To make it easy, use Adaptec's usual 64/32 mapping - */ - geo->heads = 64; - geo->sectors = 32; - geo->cylinders = aip->size >> 11; - return 0; -} - -static int acsi_ioctl( struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg ) -{ - struct gendisk *disk = inode->i_bdev->bd_disk; - struct acsi_info_struct *aip = disk->private_data; - switch (cmd) { - case SCSI_IOCTL_GET_IDLUN: - /* SCSI compatible GET_IDLUN call to get target's ID and LUN number */ - put_user( aip->target | (aip->lun << 8), - &((Scsi_Idlun *) arg)->dev_id ); - put_user( 0, &((Scsi_Idlun *) arg)->host_unique_id ); - return 0; - default: - return -EINVAL; - } -} - - -/* - * Open a device, check for read-only and lock the medium if it is - * removable. - * - * Changes by Martin Rogge, 9th Aug 1995: - * Check whether check_disk_change (and therefore revalidate_acsidisk) - * was successful. They fail when there is no medium in the drive. - * - * The problem of media being changed during an operation can be - * ignored because of the prevent_removal code. - * - * Added check for the validity of the device number. - * - */ - -static int acsi_open( struct inode * inode, struct file * filp ) -{ - struct gendisk *disk = inode->i_bdev->bd_disk; - struct acsi_info_struct *aip = disk->private_data; - - if (aip->access_count == 0 && aip->removable) { -#if 0 - aip->changed = 1; /* safety first */ -#endif - check_disk_change( inode->i_bdev ); - if (aip->changed) /* revalidate was not successful (no medium) */ - return -ENXIO; - acsi_prevent_removal(aip, 1); - } - aip->access_count++; - - if (filp && filp->f_mode) { - check_disk_change( inode->i_bdev ); - if (filp->f_mode & 2) { - if (aip->read_only) { - acsi_release( inode, filp ); - return -EROFS; - } - } - } - - return 0; -} - -/* - * Releasing a block device means we sync() it, so that it can safely - * be forgotten about... - */ - -static int acsi_release( struct inode * inode, struct file * file ) -{ - struct gendisk *disk = inode->i_bdev->bd_disk; - struct acsi_info_struct *aip = disk->private_data; - if (--aip->access_count == 0 && aip->removable) - acsi_prevent_removal(aip, 0); - return( 0 ); -} - -/* - * Prevent or allow a media change for removable devices. - */ - -static void acsi_prevent_removal(struct acsi_info_struct *aip, int flag) -{ - stdma_lock( NULL, NULL ); - - CMDSET_TARG_LUN(pa_med_rem_cmd, aip->target, aip->lun); - CMDSET_LEN( pa_med_rem_cmd, flag ); - - if (acsicmd_nodma(pa_med_rem_cmd, 0) && acsi_wait_for_IRQ(3*HZ)) - acsi_getstatus(); - /* Do not report errors -- some devices may not know this command. */ - - ENABLE_IRQ(); - stdma_release(); -} - -static int acsi_media_change(struct gendisk *disk) -{ - struct acsi_info_struct *aip = disk->private_data; - - if (!aip->removable) - return 0; - - if (aip->changed) - /* We can be sure that the medium has been changed -- REQUEST - * SENSE has reported this earlier. - */ - return 1; - - /* If the flag isn't set, make a test by reading block 0. - * If errors happen, it seems to be better to say "changed"... - */ - stdma_lock( NULL, NULL ); - CMDSET_TARG_LUN(read_cmd, aip->target, aip->lun); - CMDSET_BLOCK( read_cmd, 0 ); - CMDSET_LEN( read_cmd, 1 ); - if (acsicmd_dma(read_cmd, acsi_buffer, 1, 0, 0) && - acsi_wait_for_IRQ(3*HZ)) { - if (acsi_getstatus()) { - if (acsi_reqsense(acsi_buffer, aip->target, aip->lun)) { - if (CARTRCH_STAT(aip, acsi_buffer)) - aip->changed = 1; - } - else { - printk( KERN_ERR "%s: REQUEST SENSE failed in test for " - "medium change; assuming a change\n", disk->disk_name ); - aip->changed = 1; - } - } - } - else { - printk( KERN_ERR "%s: Test for medium changed timed out; " - "assuming a change\n", disk->disk_name); - aip->changed = 1; - } - ENABLE_IRQ(); - stdma_release(); - - /* Now, after reading a block, the changed status is surely valid. */ - return aip->changed; -} - - -static int acsi_change_blk_size( int target, int lun) - -{ int i; - - for (i=0; i<12; i++) - acsi_buffer[i] = 0; - - acsi_buffer[3] = 8; - acsi_buffer[10] = 2; - CMDSET_TARG_LUN( modeselect_cmd, target, lun); - - if (!acsicmd_dma( modeselect_cmd, acsi_buffer, 1,1,0) || - !acsi_wait_for_IRQ( 3*HZ ) || - acsi_getstatus() != 0 ) { - return(0); - } - return(1); -} - - -static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd ) - -{ - int page; - - CMDSET_TARG_LUN( modesense_cmd, target, lun ); - for (page=0; page<4; page++) { - modesense_cmd[2] = page; - if (!acsicmd_dma( modesense_cmd, acsi_buffer, 1, 0, 0 ) || - !acsi_wait_for_IRQ( 3*HZ ) || - acsi_getstatus()) - continue; - - /* read twice to jump over the second 16-byte border! */ - udelay(300); - if (acsi_wait_for_noIRQ( 20 ) && - acsicmd_nodma( modesense_cmd, 0 ) && - acsi_wait_for_IRQ( 3*HZ ) && - acsi_getstatus() == 0) - break; - } - if (page == 4) { - return(0); - } - - dma_cache_maintenance( phys_acsi_buffer, sizeof(SENSE_DATA), 0 ); - *sd = *(SENSE_DATA *)acsi_buffer; - - /* Validity check, depending on type of data */ - - switch( SENSE_TYPE(*sd) ) { - - case SENSE_TYPE_ATARI: - if (CAPACITY(*sd) == 0) - goto invalid_sense; - break; - - case SENSE_TYPE_SCSI: - if (sd->scsi.descriptor_size != 8) - goto invalid_sense; - break; - - case SENSE_TYPE_UNKNOWN: - - printk( KERN_ERR "ACSI target %d, lun %d: Cannot interpret " - "sense data\n", target, lun ); - - invalid_sense: - -#ifdef DEBUG - { int i; - printk( "Mode sense data for ACSI target %d, lun %d seem not valid:", - target, lun ); - for( i = 0; i < sizeof(SENSE_DATA); ++i ) - printk( "%02x ", (unsigned char)acsi_buffer[i] ); - printk( "\n" ); - } -#endif - return( 0 ); - } - - return( 1 ); -} - - - -/******************************************************************* - * - * Initialization - * - ********************************************************************/ - - -extern struct block_device_operations acsi_fops; - -static struct gendisk *acsi_gendisk[MAX_DEV]; - -#define MAX_SCSI_DEVICE_CODE 10 - -static const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = -{ - "Direct-Access ", - "Sequential-Access", - "Printer ", - "Processor ", - "WORM ", - "CD-ROM ", - "Scanner ", - "Optical Device ", - "Medium Changer ", - "Communications " -}; - -static void print_inquiry(unsigned char *data) -{ - int i; - - printk(KERN_INFO " Vendor: "); - for (i = 8; i < 16; i++) - { - if (data[i] >= 0x20 && i < data[4] + 5) - printk("%c", data[i]); - else - printk(" "); - } - - printk(" Model: "); - for (i = 16; i < 32; i++) - { - if (data[i] >= 0x20 && i < data[4] + 5) - printk("%c", data[i]); - else - printk(" "); - } - - printk(" Rev: "); - for (i = 32; i < 36; i++) - { - if (data[i] >= 0x20 && i < data[4] + 5) - printk("%c", data[i]); - else - printk(" "); - } - - printk("\n"); - - i = data[0] & 0x1f; - - printk(KERN_INFO " Type: %s ", (i < MAX_SCSI_DEVICE_CODE - ? scsi_device_types[i] - : "Unknown ")); - printk(" ANSI SCSI revision: %02x", data[2] & 0x07); - if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1) - printk(" CCS\n"); - else - printk("\n"); -} - - -/* - * Changes by Martin Rogge, 9th Aug 1995: - * acsi_devinit has been taken out of acsi_geninit, because it needs - * to be called from revalidate_acsidisk. The result of request sense - * is now checked for DRIVE NOT READY. - * - * The structure *aip is only valid when acsi_devinit returns - * DEV_SUPPORTED. - * - */ - -#define DEV_NONE 0 -#define DEV_UNKNOWN 1 -#define DEV_SUPPORTED 2 -#define DEV_SLM 3 - -static int acsi_devinit(struct acsi_info_struct *aip) -{ - int status, got_inquiry; - SENSE_DATA sense; - unsigned char reqsense, extsense; - - /*****************************************************************/ - /* Do a TEST UNIT READY command to test the presence of a device */ - /*****************************************************************/ - - CMDSET_TARG_LUN(tur_cmd, aip->target, aip->lun); - if (!acsicmd_nodma(tur_cmd, 0)) { - /* timed out -> no device here */ -#ifdef DEBUG_DETECT - printk("target %d lun %d: timeout\n", aip->target, aip->lun); -#endif - return DEV_NONE; - } - - /*************************/ - /* Read the ACSI status. */ - /*************************/ - - status = acsi_getstatus(); - if (status) { - if (status == 0x12) { - /* The SLM printer should be the only device that - * responds with the error code in the status byte. In - * correct status bytes, bit 4 is never set. - */ - printk( KERN_INFO "Detected SLM printer at id %d lun %d\n", - aip->target, aip->lun); - return DEV_SLM; - } - /* ignore CHECK CONDITION, since some devices send a - UNIT ATTENTION */ - if ((status & 0x1e) != 0x2) { -#ifdef DEBUG_DETECT - printk("target %d lun %d: status %d\n", - aip->target, aip->lun, status); -#endif - return DEV_UNKNOWN; - } - } - - /*******************************/ - /* Do a REQUEST SENSE command. */ - /*******************************/ - - if (!acsi_reqsense(acsi_buffer, aip->target, aip->lun)) { - printk( KERN_WARNING "acsi_reqsense failed\n"); - acsi_buffer[0] = 0; - acsi_buffer[2] = UNIT_ATTENTION; - } - reqsense = acsi_buffer[0]; - extsense = acsi_buffer[2] & 0xf; - if (status) { - if ((reqsense & 0x70) == 0x70) { /* extended sense */ - if (extsense != UNIT_ATTENTION && - extsense != NOT_READY) { -#ifdef DEBUG_DETECT - printk("target %d lun %d: extended sense %d\n", - aip->target, aip->lun, extsense); -#endif - return DEV_UNKNOWN; - } - } - else { - if (reqsense & 0x7f) { -#ifdef DEBUG_DETECT - printk("target %d lun %d: sense %d\n", - aip->target, aip->lun, reqsense); -#endif - return DEV_UNKNOWN; - } - } - } - else - if (reqsense == 0x4) { /* SH204 Bug workaround */ -#ifdef DEBUG_DETECT - printk("target %d lun %d status=0 sense=4\n", - aip->target, aip->lun); -#endif - return DEV_UNKNOWN; - } - - /***********************************************************/ - /* Do an INQUIRY command to get more infos on this device. */ - /***********************************************************/ - - /* Assume default values */ - aip->removable = 1; - aip->read_only = 0; - aip->old_atari_disk = 0; - aip->changed = (extsense == NOT_READY); /* medium inserted? */ - aip->size = DEFAULT_SIZE; - got_inquiry = 0; - /* Fake inquiry result for old atari disks */ - memcpy(acsi_buffer, "\000\000\001\000 Adaptec 40xx" - " ", 40); - CMDSET_TARG_LUN(inquiry_cmd, aip->target, aip->lun); - if (acsicmd_dma(inquiry_cmd, acsi_buffer, 1, 0, 0) && - acsi_getstatus() == 0) { - acsicmd_nodma(inquiry_cmd, 0); - acsi_getstatus(); - dma_cache_maintenance( phys_acsi_buffer, 256, 0 ); - got_inquiry = 1; - aip->removable = !!(acsi_buffer[1] & 0x80); - } - if (aip->type == NONE) /* only at boot time */ - print_inquiry(acsi_buffer); - switch(acsi_buffer[0]) { - case TYPE_DISK: - aip->type = HARDDISK; - break; - case TYPE_ROM: - aip->type = CDROM; - aip->read_only = 1; - break; - default: - return DEV_UNKNOWN; - } - /****************************/ - /* Do a MODE SENSE command. */ - /****************************/ - - if (!acsi_mode_sense(aip->target, aip->lun, &sense)) { - printk( KERN_WARNING "No mode sense data.\n" ); - return DEV_UNKNOWN; - } - if ((SECTOR_SIZE(sense) != 512) && - ((aip->type != CDROM) || - !acsi_change_blk_size(aip->target, aip->lun) || - !acsi_mode_sense(aip->target, aip->lun, &sense) || - (SECTOR_SIZE(sense) != 512))) { - printk( KERN_WARNING "Sector size != 512 not supported.\n" ); - return DEV_UNKNOWN; - } - /* There are disks out there that claim to have 0 sectors... */ - if (CAPACITY(sense)) - aip->size = CAPACITY(sense); /* else keep DEFAULT_SIZE */ - if (!got_inquiry && SENSE_TYPE(sense) == SENSE_TYPE_ATARI) { - /* If INQUIRY failed and the sense data suggest an old - * Atari disk (SH20x, Megafile), the disk is not removable - */ - aip->removable = 0; - aip->old_atari_disk = 1; - } - - /******************/ - /* We've done it. */ - /******************/ - - return DEV_SUPPORTED; -} - -EXPORT_SYMBOL(acsi_delay_start); -EXPORT_SYMBOL(acsi_delay_end); -EXPORT_SYMBOL(acsi_wait_for_IRQ); -EXPORT_SYMBOL(acsi_wait_for_noIRQ); -EXPORT_SYMBOL(acsicmd_nodma); -EXPORT_SYMBOL(acsi_getstatus); -EXPORT_SYMBOL(acsi_buffer); -EXPORT_SYMBOL(phys_acsi_buffer); - -#ifdef CONFIG_ATARI_SLM_MODULE -void acsi_attach_SLMs( int (*attach_func)( int, int ) ); - -EXPORT_SYMBOL(acsi_extstatus); -EXPORT_SYMBOL(acsi_end_extstatus); -EXPORT_SYMBOL(acsi_extcmd); -EXPORT_SYMBOL(acsi_attach_SLMs); - -/* to remember IDs of SLM devices, SLM module is loaded later - * (index is target#, contents is lun#, -1 means "no SLM") */ -int SLM_devices[8]; -#endif - -static struct block_device_operations acsi_fops = { - .owner = THIS_MODULE, - .open = acsi_open, - .release = acsi_release, - .ioctl = acsi_ioctl, - .getgeo = acsi_getgeo, - .media_changed = acsi_media_change, - .revalidate_disk= acsi_revalidate, -}; - -#ifdef CONFIG_ATARI_SLM_MODULE -/* call attach_slm() for each device that is a printer; needed for init of SLM - * driver as a module, since it's not yet present if acsi.c is inited and thus - * the bus gets scanned. */ -void acsi_attach_SLMs( int (*attach_func)( int, int ) ) -{ - int i, n = 0; - - for( i = 0; i < 8; ++i ) - if (SLM_devices[i] >= 0) - n += (*attach_func)( i, SLM_devices[i] ); - printk( KERN_INFO "Found %d SLM printer(s) total.\n", n ); -} -#endif /* CONFIG_ATARI_SLM_MODULE */ - - -int acsi_init( void ) -{ - int err = 0; - int i, target, lun; - struct acsi_info_struct *aip; -#ifdef CONFIG_ATARI_SLM - int n_slm = 0; -#endif - if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ACSI)) - return 0; - if (register_blkdev(ACSI_MAJOR, "ad")) { - err = -EBUSY; - goto out1; - } - if (!(acsi_buffer = - (char *)atari_stram_alloc(ACSI_BUFFER_SIZE, "acsi"))) { - err = -ENOMEM; - printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" ); - goto out2; - } - phys_acsi_buffer = virt_to_phys( acsi_buffer ); - STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000; - - acsi_queue = blk_init_queue(do_acsi_request, &acsi_lock); - if (!acsi_queue) { - err = -ENOMEM; - goto out2a; - } -#ifdef CONFIG_ATARI_SLM - err = slm_init(); -#endif - if (err) - goto out3; - - printk( KERN_INFO "Probing ACSI devices:\n" ); - NDevices = 0; -#ifdef CONFIG_ATARI_SLM_MODULE - for( i = 0; i < 8; ++i ) - SLM_devices[i] = -1; -#endif - stdma_lock(NULL, NULL); - - for (target = 0; target < 8 && NDevices < MAX_DEV; ++target) { - lun = 0; - do { - aip = &acsi_info[NDevices]; - aip->type = NONE; - aip->target = target; - aip->lun = lun; - i = acsi_devinit(aip); - switch (i) { - case DEV_SUPPORTED: - printk( KERN_INFO "Detected "); - switch (aip->type) { - case HARDDISK: - printk("disk"); - break; - case CDROM: - printk("cdrom"); - break; - default: - } - printk(" ad%c at id %d lun %d ", - 'a' + NDevices, target, lun); - if (aip->removable) - printk("(removable) "); - if (aip->read_only) - printk("(read-only) "); - if (aip->size == DEFAULT_SIZE) - printk(" unkown size, using default "); - printk("%ld MByte\n", - (aip->size*512+1024*1024/2)/(1024*1024)); - NDevices++; - break; - case DEV_SLM: -#ifdef CONFIG_ATARI_SLM - n_slm += attach_slm( target, lun ); - break; -#endif -#ifdef CONFIG_ATARI_SLM_MODULE - SLM_devices[target] = lun; - break; -#endif - /* neither of the above: fall through to unknown device */ - case DEV_UNKNOWN: - printk( KERN_INFO "Detected unsupported device at " - "id %d lun %d\n", target, lun); - break; - } - } -#ifdef CONFIG_ACSI_MULTI_LUN - while (i != DEV_NONE && ++lun < MAX_LUN); -#else - while (0); -#endif - } - - /* reenable interrupt */ - ENABLE_IRQ(); - stdma_release(); - -#ifndef CONFIG_ATARI_SLM - printk( KERN_INFO "Found %d ACSI device(s) total.\n", NDevices ); -#else - printk( KERN_INFO "Found %d ACSI device(s) and %d SLM printer(s) total.\n", - NDevices, n_slm ); -#endif - err = -ENOMEM; - for( i = 0; i < NDevices; ++i ) { - acsi_gendisk[i] = alloc_disk(16); - if (!acsi_gendisk[i]) - goto out4; - } - - for( i = 0; i < NDevices; ++i ) { - struct gendisk *disk = acsi_gendisk[i]; - sprintf(disk->disk_name, "ad%c", 'a'+i); - aip = &acsi_info[NDevices]; - disk->major = ACSI_MAJOR; - disk->first_minor = i << 4; - if (acsi_info[i].type != HARDDISK) - disk->minors = 1; - disk->fops = &acsi_fops; - disk->private_data = &acsi_info[i]; - set_capacity(disk, acsi_info[i].size); - disk->queue = acsi_queue; - add_disk(disk); - } - return 0; -out4: - while (i--) - put_disk(acsi_gendisk[i]); -out3: - blk_cleanup_queue(acsi_queue); -out2a: - atari_stram_free( acsi_buffer ); -out2: - unregister_blkdev( ACSI_MAJOR, "ad" ); -out1: - return err; -} - - -#ifdef MODULE - -MODULE_LICENSE("GPL"); - -int init_module(void) -{ - int err; - - if ((err = acsi_init())) - return( err ); - printk( KERN_INFO "ACSI driver loaded as module.\n"); - return( 0 ); -} - -void cleanup_module(void) -{ - int i; - del_timer( &acsi_timer ); - blk_cleanup_queue(acsi_queue); - atari_stram_free( acsi_buffer ); - - if (unregister_blkdev( ACSI_MAJOR, "ad" ) != 0) - printk( KERN_ERR "acsi: cleanup_module failed\n"); - - for (i = 0; i < NDevices; i++) { - del_gendisk(acsi_gendisk[i]); - put_disk(acsi_gendisk[i]); - } -} -#endif - -/* - * This routine is called to flush all partitions and partition tables - * for a changed scsi disk, and then re-read the new partition table. - * If we are revalidating a disk because of a media change, then we - * enter with usage == 0. If we are using an ioctl, we automatically have - * usage == 1 (we need an open channel to use an ioctl :-), so this - * is our limit. - * - * Changes by Martin Rogge, 9th Aug 1995: - * got cd-roms to work by calling acsi_devinit. There are only two problems: - * First, if there is no medium inserted, the status will remain "changed". - * That is no problem at all, but our design of three-valued logic (medium - * changed, medium not changed, no medium inserted). - * Secondly the check could fail completely and the drive could deliver - * nonsensical data, which could mess up the acsi_info[] structure. In - * that case we try to make the entry safe. - * - */ - -static int acsi_revalidate(struct gendisk *disk) -{ - struct acsi_info_struct *aip = disk->private_data; - stdma_lock( NULL, NULL ); - if (acsi_devinit(aip) != DEV_SUPPORTED) { - printk( KERN_ERR "ACSI: revalidate failed for target %d lun %d\n", - aip->target, aip->lun); - aip->size = 0; - aip->read_only = 1; - aip->removable = 1; - aip->changed = 1; /* next acsi_open will try again... */ - } - - ENABLE_IRQ(); - stdma_release(); - set_capacity(disk, aip->size); - return 0; -} diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 27a139025ce..6ce8b897e26 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -1363,7 +1363,7 @@ static void redo_fd_request(void) #ifdef DEBUG printk("fd: sector %ld + %d requested for %s\n", CURRENT->sector,cnt, - (CURRENT->cmd==READ)?"read":"write"); + (rq_data_dir(CURRENT) == READ) ? "read" : "write"); #endif block = CURRENT->sector + cnt; if ((int)block > floppy->blocks) { diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 5acc6c44aea..0fcad430474 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -87,6 +87,7 @@ static const struct pci_device_id cciss_pci_device_id[] = { {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, 0x103C, 0x3214}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, 0x103C, 0x3215}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x3237}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, 0x103C, 0x323D}, {PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0}, {0,} @@ -119,6 +120,7 @@ static struct board_type products[] = { {0x3214103C, "Smart Array E200i", &SA5_access, 120}, {0x3215103C, "Smart Array E200i", &SA5_access, 120}, {0x3237103C, "Smart Array E500", &SA5_access, 512}, + {0x323D103C, "Smart Array P700m", &SA5_access, 512}, {0xFFFF103C, "Unknown Smart Array", &SA5_access, 120}, }; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 0ed5470d253..4503290da40 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -74,6 +74,7 @@ #include <linux/highmem.h> #include <linux/gfp.h> #include <linux/kthread.h> +#include <linux/splice.h> #include <asm/uaccess.h> @@ -401,50 +402,73 @@ struct lo_read_data { }; static int -lo_read_actor(read_descriptor_t *desc, struct page *page, - unsigned long offset, unsigned long size) +lo_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, + struct splice_desc *sd) { - unsigned long count = desc->count; - struct lo_read_data *p = desc->arg.data; + struct lo_read_data *p = sd->u.data; struct loop_device *lo = p->lo; + struct page *page = buf->page; sector_t IV; + size_t size; + int ret; - IV = ((sector_t) page->index << (PAGE_CACHE_SHIFT - 9))+(offset >> 9); + ret = buf->ops->confirm(pipe, buf); + if (unlikely(ret)) + return ret; - if (size > count) - size = count; + IV = ((sector_t) page->index << (PAGE_CACHE_SHIFT - 9)) + + (buf->offset >> 9); + size = sd->len; + if (size > p->bsize) + size = p->bsize; - if (lo_do_transfer(lo, READ, page, offset, p->page, p->offset, size, IV)) { - size = 0; + if (lo_do_transfer(lo, READ, page, buf->offset, p->page, p->offset, size, IV)) { printk(KERN_ERR "loop: transfer error block %ld\n", page->index); - desc->error = -EINVAL; + size = -EINVAL; } flush_dcache_page(p->page); - desc->count = count - size; - desc->written += size; - p->offset += size; + if (size > 0) + p->offset += size; + return size; } static int +lo_direct_splice_actor(struct pipe_inode_info *pipe, struct splice_desc *sd) +{ + return __splice_from_pipe(pipe, sd, lo_splice_actor); +} + +static int do_lo_receive(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos) { struct lo_read_data cookie; + struct splice_desc sd; struct file *file; - int retval; + long retval; cookie.lo = lo; cookie.page = bvec->bv_page; cookie.offset = bvec->bv_offset; cookie.bsize = bsize; + + sd.len = 0; + sd.total_len = bvec->bv_len; + sd.flags = 0; + sd.pos = pos; + sd.u.data = &cookie; + file = lo->lo_backing_file; - retval = file->f_op->sendfile(file, &pos, bvec->bv_len, - lo_read_actor, &cookie); - return (retval < 0)? retval: 0; + retval = splice_direct_to_actor(file, &sd, lo_direct_splice_actor); + + if (retval < 0) + return retval; + + return 0; } static int @@ -679,8 +703,8 @@ static int loop_change_fd(struct loop_device *lo, struct file *lo_file, if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) goto out_putf; - /* new backing store needs to support loop (eg sendfile) */ - if (!inode->i_fop->sendfile) + /* new backing store needs to support loop (eg splice_read) */ + if (!inode->i_fop->splice_read) goto out_putf; /* size of the new backing store needs to be the same */ @@ -760,7 +784,7 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file, * If we can't read - sorry. If we only can't write - well, * it's going to be read-only. */ - if (!file->f_op->sendfile) + if (!file->f_op->splice_read) goto out_putf; if (aops->prepare_write && aops->commit_write) lo_flags |= LO_FLAGS_USE_AOPS; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 069ae39a9cd..c575fb1d585 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -416,7 +416,7 @@ static void nbd_clear_que(struct nbd_device *lo) /* * We always wait for result of write, for now. It would be nice to make it optional * in future - * if ((req->cmd == WRITE) && (lo->flags & NBD_WRITE_NOCHK)) + * if ((rq_data_dir(req) == WRITE) && (lo->flags & NBD_WRITE_NOCHK)) * { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); } */ diff --git a/drivers/cdrom/Kconfig b/drivers/cdrom/Kconfig deleted file mode 100644 index 4b12e9031fb..00000000000 --- a/drivers/cdrom/Kconfig +++ /dev/null @@ -1,213 +0,0 @@ -# -# CDROM driver configuration -# - -menu "Old CD-ROM drivers (not SCSI, not IDE)" - depends on ISA && BLOCK - -config CD_NO_IDESCSI - bool "Support non-SCSI/IDE/ATAPI CDROM drives" - ---help--- - If you have a CD-ROM drive that is neither SCSI nor IDE/ATAPI, say Y - here, otherwise N. Read the CD-ROM-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about these CD-ROM drives. If you are unsure what you - have, say Y and find out whether you have one of the following - drives. - - For each of these drivers, a <file:Documentation/cdrom/{driver_name}> - exists. Especially in cases where you do not know exactly which kind - of drive you have you should read there. Most of these drivers use a - file drivers/cdrom/{driver_name}.h where you can define your - interface parameters and switch some internal goodies. - - To compile these CD-ROM drivers as a module, choose M instead of Y. - - If you want to use any of these CD-ROM drivers, you also have to - answer Y or M to "ISO 9660 CD-ROM file system support" below (this - answer will get "defaulted" for you if you enable any of the Linux - CD-ROM drivers). - -config AZTCD - tristate "Aztech/Orchid/Okano/Wearnes/TXC/CyDROM CDROM support" - depends on CD_NO_IDESCSI - ---help--- - This is your driver if you have an Aztech CDA268-01A, Orchid - CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCD-ROM CR520 or - CR540 CD-ROM drive. This driver -- just like all these CD-ROM - drivers -- is NOT for CD-ROM drives with IDE/ATAPI interfaces, such - as Aztech CDA269-031SE. Please read the file - <file:Documentation/cdrom/aztcd>. - - If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM - file system support" below, because that's the file system used on - CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called aztcd. - -config GSCD - tristate "Goldstar R420 CDROM support" - depends on CD_NO_IDESCSI - ---help--- - If this is your CD-ROM drive, say Y here. As described in the file - <file:Documentation/cdrom/gscd>, you might have to change a setting - in the file <file:drivers/cdrom/gscd.h> before compiling the - kernel. Please read the file <file:Documentation/cdrom/gscd>. - - If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM - file system support" below, because that's the file system used on - CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called gscd. - -config SBPCD - tristate "Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support" - depends on CD_NO_IDESCSI && BROKEN_ON_SMP - ---help--- - This driver supports most of the drives which use the Panasonic or - Sound Blaster interface. Please read the file - <file:Documentation/cdrom/sbpcd>. - - The Matsushita CR-521, CR-522, CR-523, CR-562, CR-563 drives - (sometimes labeled "Creative"), the Creative Labs CD200, the - Longshine LCS-7260, the "IBM External ISA CD-ROM" (in fact a CR-56x - model), the TEAC CD-55A fall under this category. Some other - "electrically compatible" drives (Vertos, Genoa, some Funai models) - are currently not supported; for the Sanyo H94A drive currently a - separate driver (asked later) is responsible. Most drives have a - uniquely shaped faceplate, with a caddyless motorized drawer, but - without external brand markings. The older CR-52x drives have a - caddy and manual loading/eject, but still no external markings. The - driver is able to do an extended auto-probing for interface - addresses and drive types; this can help to find facts in cases you - are not sure, but can consume some time during the boot process if - none of the supported drives gets found. Once your drive got found, - you should enter the reported parameters into - <file:drivers/cdrom/sbpcd.h> and set "DISTRIBUTION 0" there. - - This driver can support up to four CD-ROM controller cards, and each - card can support up to four CD-ROM drives; if you say Y here, you - will be asked how many controller cards you have. If compiled as a - module, only one controller card (but with up to four drives) is - usable. - - If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM - file system support" below, because that's the file system used on - CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called sbpcd. - -config MCDX - tristate "Mitsumi CDROM support" - depends on CD_NO_IDESCSI - ---help--- - Use this driver if you want to be able to use your Mitsumi LU-005, - FX-001 or FX-001D CD-ROM drive. - - Please read the file <file:Documentation/cdrom/mcdx>. - - If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM - file system support" below, because that's the file system used on - CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called mcdx. - -config OPTCD - tristate "Optics Storage DOLPHIN 8000AT CDROM support" - depends on CD_NO_IDESCSI - ---help--- - This is the driver for the 'DOLPHIN' drive with a 34-pin Sony - compatible interface. It also works with the Lasermate CR328A. If - you have one of those, say Y. This driver does not work for the - Optics Storage 8001 drive; use the IDE-ATAPI CD-ROM driver for that - one. Please read the file <file:Documentation/cdrom/optcd>. - - If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM - file system support" below, because that's the file system used on - CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called optcd. - -config CM206 - tristate "Philips/LMS CM206 CDROM support" - depends on CD_NO_IDESCSI && BROKEN_ON_SMP - ---help--- - If you have a Philips/LMS CD-ROM drive cm206 in combination with a - cm260 host adapter card, say Y here. Please also read the file - <file:Documentation/cdrom/cm206>. - - If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM - file system support" below, because that's the file system used on - CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called cm206. - -config SJCD - tristate "Sanyo CDR-H94A CDROM support" - depends on CD_NO_IDESCSI - help - If this is your CD-ROM drive, say Y here and read the file - <file:Documentation/cdrom/sjcd>. You should then also say Y or M to - "ISO 9660 CD-ROM file system support" below, because that's the - file system used on CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called sjcd. - -config ISP16_CDI - tristate "ISP16/MAD16/Mozart soft configurable cdrom interface support" - depends on CD_NO_IDESCSI - ---help--- - These are sound cards with built-in cdrom interfaces using the OPTi - 82C928 or 82C929 chips. Say Y here to have them detected and - possibly configured at boot time. In addition, You'll have to say Y - to a driver for the particular cdrom drive you have attached to the - card. Read <file:Documentation/cdrom/isp16> for details. - - To compile this driver as a module, choose M here: the - module will be called isp16. - -config CDU31A - tristate "Sony CDU31A/CDU33A CDROM support" - depends on CD_NO_IDESCSI && BROKEN_ON_SMP - ---help--- - These CD-ROM drives have a spring-pop-out caddyless drawer, and a - rectangular green LED centered beneath it. NOTE: these CD-ROM - drives will not be auto detected by the kernel at boot time; you - have to provide the interface address as an option to the kernel at - boot time as described in <file:Documentation/cdrom/cdu31a> or fill - in your parameters into <file:drivers/cdrom/cdu31a.c>. Try "man - bootparam" or see the documentation of your boot loader (lilo or - loadlin) about how to pass options to the kernel. - - If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM - file system support" below, because that's the file system used on - CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called cdu31a. - -config CDU535 - tristate "Sony CDU535 CDROM support" - depends on CD_NO_IDESCSI - ---help--- - This is the driver for the older Sony CDU-535 and CDU-531 CD-ROM - drives. Please read the file <file:Documentation/cdrom/sonycd535>. - - If you say Y here, you should also say Y or M to "ISO 9660 CD-ROM - file system support" below, because that's the file system used on - CD-ROMs. - - To compile this driver as a module, choose M here: the - module will be called sonycd535. - -endmenu diff --git a/drivers/cdrom/Makefile b/drivers/cdrom/Makefile index d1d1e5a4be7..774c180a4e1 100644 --- a/drivers/cdrom/Makefile +++ b/drivers/cdrom/Makefile @@ -10,14 +10,4 @@ obj-$(CONFIG_BLK_DEV_SR) += cdrom.o obj-$(CONFIG_PARIDE_PCD) += cdrom.o obj-$(CONFIG_CDROM_PKTCDVD) += cdrom.o -obj-$(CONFIG_AZTCD) += aztcd.o -obj-$(CONFIG_CDU31A) += cdu31a.o cdrom.o -obj-$(CONFIG_CM206) += cm206.o cdrom.o -obj-$(CONFIG_GSCD) += gscd.o -obj-$(CONFIG_ISP16_CDI) += isp16.o -obj-$(CONFIG_MCDX) += mcdx.o cdrom.o -obj-$(CONFIG_OPTCD) += optcd.o -obj-$(CONFIG_SBPCD) += sbpcd.o cdrom.o -obj-$(CONFIG_SJCD) += sjcd.o -obj-$(CONFIG_CDU535) += sonycd535.o obj-$(CONFIG_VIOCD) += viocd.o cdrom.o diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c deleted file mode 100644 index 1f9fb7a9670..00000000000 --- a/drivers/cdrom/aztcd.c +++ /dev/null @@ -1,2492 +0,0 @@ -#define AZT_VERSION "2.60" - -/* $Id: aztcd.c,v 2.60 1997/11/29 09:51:19 root Exp root $ - linux/drivers/block/aztcd.c - Aztech CD268 CDROM driver - - Copyright (C) 1994-98 Werner Zimmermann(Werner.Zimmermann@fht-esslingen.de) - - based on Mitsumi CDROM driver by Martin Hariss and preworks by - Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby - Schirmer. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - 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. - - HISTORY - V0.0 Adaption to Aztech CD268-01A Version 1.3 - Version is PRE_ALPHA, unresolved points: - 1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW - thus driver causes CPU overhead and is very slow - 2. could not find a way to stop the drive, when it is - in data read mode, therefore I had to set - msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one - frame can be read in sequence, this is also the reason for - 3. getting 'timeout in state 4' messages, but nevertheless - it works - W.Zimmermann, Oct. 31, 1994 - V0.1 Version is ALPHA, problems #2 and #3 resolved. - W.Zimmermann, Nov. 3, 1994 - V0.2 Modification to some comments, debugging aids for partial test - with Borland C under DOS eliminated. Timer interrupt wait - STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented; - use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_ - SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy - waiting seems better to me than interrupt rescheduling. - Besides that, when used in the wrong place, STEN_LOW_WAIT causes - kernel panic. - In function aztPlay command ACMD_PLAY_AUDIO added, should make - audio functions work. The Aztech drive needs different commands - to read data tracks and play audio tracks. - W.Zimmermann, Nov. 8, 1994 - V0.3 Recognition of missing drive during boot up improved (speeded up). - W.Zimmermann, Nov. 13, 1994 - V0.35 Rewrote the control mechanism in azt_poll (formerly mcd_poll) - including removal of all 'goto' commands. :-); - J. Nardone, Nov. 14, 1994 - V0.4 Renamed variables and constants to 'azt' instead of 'mcd'; had - to make some "compatibility" defines in azt.h; please note, - that the source file was renamed to azt.c, the include file to - azt.h - Speeded up drive recognition during init (will be a little bit - slower than before if no drive is installed!); suggested by - Robby Schirmer. - read_count declared volatile and set to AZT_BUF_SIZ to make - drive faster (now 300kB/sec, was 60kB/sec before, measured - by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096'; - different AZT_BUF_SIZes were test, above 16 no further im- - provement seems to be possible; suggested by E.Moenkeberg. - W.Zimmermann, Nov. 18, 1994 - V0.42 Included getAztStatus command in GetQChannelInfo() to allow - reading Q-channel info on audio disks, if drive is stopped, - and some other bug fixes in the audio stuff, suggested by - Robby Schirmer. - Added more ioctls (reading data in mode 1 and mode 2). - Completely removed the old azt_poll() routine. - Detection of ORCHID CDS-3110 in aztcd_init implemented. - Additional debugging aids (see the readme file). - W.Zimmermann, Dec. 9, 1994 - V0.50 Autodetection of drives implemented. - W.Zimmermann, Dec. 12, 1994 - V0.52 Prepared for including in the standard kernel, renamed most - variables to contain 'azt', included autoconf.h - W.Zimmermann, Dec. 16, 1994 - V0.6 Version for being included in the standard Linux kernel. - Renamed source and header file to aztcd.c and aztcd.h - W.Zimmermann, Dec. 24, 1994 - V0.7 Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case - CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl, - which causes kernel crashes when playing audio, changed - include-files (config.h instead of autoconf.h, removed - delay.h) - W.Zimmermann, Jan. 8, 1995 - V0.72 Some more modifications for adaption to the standard kernel. - W.Zimmermann, Jan. 16, 1995 - V0.80 aztcd is now part of the standard kernel since version 1.1.83. - Modified the SET_TIMER and CLEAR_TIMER macros to comply with - the new timer scheme. - W.Zimmermann, Jan. 21, 1995 - V0.90 Included CDROMVOLCTRL, but with my Aztech drive I can only turn - the channels on and off. If it works better with your drive, - please mail me. Also implemented ACMD_CLOSE for CDROMSTART. - W.Zimmermann, Jan. 24, 1995 - V1.00 Implemented close and lock tray commands. Patches supplied by - Frank Racis - Added support for loadable MODULEs, so aztcd can now also be - loaded by insmod and removed by rmmod during run time - Werner Zimmermann, Mar. 24, 95 - V1.10 Implemented soundcard configuration for Orchid CDS-3110 drives - connected to Soundwave32 cards. Release for LST 2.1. - (still experimental) - Werner Zimmermann, May 8, 95 - V1.20 Implemented limited support for DOSEMU0.60's cdrom.c. Now it works, but - sometimes DOSEMU may hang for 30 seconds or so. A fully functional ver- - sion needs an update of Dosemu0.60's cdrom.c, which will come with the - next revision of Dosemu. - Also Soundwave32 support now works. - Werner Zimmermann, May 22, 95 - V1.30 Auto-eject feature. Inspired by Franc Racis (racis@psu.edu) - Werner Zimmermann, July 4, 95 - V1.40 Started multisession support. Implementation copied from mcdx.c - by Heiko Schlittermann. Not tested yet. - Werner Zimmermann, July 15, 95 - V1.50 Implementation of ioctl CDROMRESET, continued multisession, began - XA, but still untested. Heavy modifications to drive status de- - tection. - Werner Zimmermann, July 25, 95 - V1.60 XA support now should work. Speeded up drive recognition in cases, - where no drive is installed. - Werner Zimmermann, August 8, 1995 - V1.70 Multisession support now is completed, but there is still not - enough testing done. If you can test it, please contact me. For - details please read Documentation/cdrom/aztcd - Werner Zimmermann, August 19, 1995 - V1.80 Modification to suit the new kernel boot procedure introduced - with kernel 1.3.33. Will definitely not work with older kernels. - Programming done by Linus himself. - Werner Zimmermann, October 11, 1995 - V1.90 Support for Conrad TXC drives, thank's to Jochen Kunz and Olaf Kaluza. - Werner Zimmermann, October 21, 1995 - V2.00 Changed #include "blk.h" to <linux/blk.h> as the directory - structure was changed. README.aztcd is now /usr/src/docu- - mentation/cdrom/aztcd - Werner Zimmermann, November 10, 95 - V2.10 Started to modify azt_poll to prevent reading beyond end of - tracks. - Werner Zimmermann, December 3, 95 - V2.20 Changed some comments - Werner Zimmermann, April 1, 96 - V2.30 Implemented support for CyCDROM CR520, CR940, Code for CR520 - delivered by H.Berger with preworks by E.Moenkeberg. - Werner Zimmermann, April 29, 96 - V2.40 Reorganized the placement of functions in the source code file - to reflect the layered approach; did not actually change code - Werner Zimmermann, May 1, 96 - V2.50 Heiko Eissfeldt suggested to remove some VERIFY_READs in - aztcd_ioctl; check_aztcd_media_change modified - Werner Zimmermann, May 16, 96 - V2.60 Implemented Auto-Probing; made changes for kernel's 2.1.xx blocksize - Adaption to linux kernel > 2.1.0 - Werner Zimmermann, Nov 29, 97 - - November 1999 -- Make kernel-parameter implementation work with 2.3.x - Removed init_module & cleanup_module in favor of - module_init & module_exit. - Torben Mathiasen <tmm@image.dk> -*/ - -#include <linux/blkdev.h> -#include "aztcd.h" - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/mm.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/cdrom.h> -#include <linux/ioport.h> -#include <linux/string.h> -#include <linux/major.h> - -#include <linux/init.h> - -#include <asm/system.h> -#include <asm/io.h> - -#include <asm/uaccess.h> - -/*########################################################################### - Defines - ########################################################################### -*/ - -#define MAJOR_NR AZTECH_CDROM_MAJOR -#define QUEUE (azt_queue) -#define CURRENT elv_next_request(azt_queue) -#define SET_TIMER(func, jifs) delay_timer.expires = jiffies + (jifs); \ - delay_timer.function = (void *) (func); \ - add_timer(&delay_timer); - -#define CLEAR_TIMER del_timer(&delay_timer); - -#define RETURNM(message,value) {printk("aztcd: Warning: %s failed\n",message);\ - return value;} -#define RETURN(message) {printk("aztcd: Warning: %s failed\n",message);\ - return;} - -/* Macros to switch the IDE-interface to the slave device and back to the master*/ -#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); -#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_DEBUG -#define AZT_DEBUG_MULTISESSION -#endif - -static struct request_queue *azt_queue; - -static int current_valid(void) -{ - return CURRENT && - CURRENT->cmd == READ && - CURRENT->sector != -1; -} - -#define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA) -#define AZT_BUF_SIZ 16 - -#define READ_TIMEOUT 3000 - -#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_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 */ -}; - -/*########################################################################## - Global Variables - ########################################################################## -*/ -static int aztPresent = 0; - -static volatile int azt_transfer_is_active = 0; - -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 */ -#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 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 int azt_st_old = 0; -#endif -static volatile enum azt_read_modes azt_read_mode = AZT_MODE_1; - -static int azt_mode = -1; -static volatile int azt_read_count = 1; - -static int azt_port = AZT_BASE_ADDR; - -module_param(azt_port, int, 0); - -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 int AztTimeout, AztTries; -static DECLARE_WAIT_QUEUE_HEAD(azt_waitq); -static DEFINE_TIMER(delay_timer, NULL, 0, 0); - -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 char aztDiskChanged = 1; -static char aztTocUpToDate = 0; - -static unsigned char aztIndatum; -static unsigned long aztTimeOutCount; -static int aztCmd = 0; - -static DEFINE_SPINLOCK(aztSpin); - -/*########################################################################### - Function Prototypes - ########################################################################### -*/ -/* CDROM Drive Low Level I/O Functions */ -static void aztStatTimer(void); - -/* CDROM Drive Command Functions */ -static int aztGetDiskInfo(void); -#if AZT_MULTISESSION -static int aztGetMultiDiskInfo(void); -#endif -static int aztGetToc(int multi); - -/* Kernel Interface Functions */ -static int check_aztcd_media_change(struct gendisk *disk); -static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, - unsigned long arg); -static int aztcd_open(struct inode *ip, struct file *fp); -static int aztcd_release(struct inode *inode, struct file *file); - -static struct block_device_operations azt_fops = { - .owner = THIS_MODULE, - .open = aztcd_open, - .release = aztcd_release, - .ioctl = aztcd_ioctl, - .media_changed = check_aztcd_media_change, -}; - -/* Aztcd State Machine: Controls Drive Operating State */ -static void azt_poll(void); - -/* Miscellaneous support functions */ -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); - -/*########################################################################## - CDROM Drive Low Level I/O Functions - ########################################################################## -*/ -/* Macros for the drive hardware interface handshake, these macros use - busy waiting */ -/* Wait for OP_OK = drive answers with AFL_OP_OK after receiving a command*/ -# define OP_OK op_ok() -static 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); -} - -/* Wait for PA_OK = drive answers with AFL_PA_OK after receiving parameters*/ -#if 0 -# define PA_OK pa_ok() -static 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); -} -#endif - -/* Wait for STEN=Low = handshake signal 'AFL_.._OK available or command executed*/ -# define STEN_LOW sten_low() -static 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); -} - -/* Wait for DTEN=Low = handshake signal 'Data available'*/ -# define DTEN_LOW dten_low() -static 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); -} - -/* - * Macro for timer wait on STEN=Low, should only be used for 'slow' commands; - * may cause kernel panic when used in the wrong place -*/ -#define STEN_LOW_WAIT statusAzt() -static 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; -} - -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); -} - -/*########################################################################## - CDROM Drive Command Functions - ########################################################################## -*/ -/* - * Send a single command, return -1 on error, else 0 -*/ -static int aztSendCmd(int cmd) -{ - unsigned char data; - int retry; - -#ifdef AZT_DEBUG - 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); -} - -/* - * 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; - -#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); -} - -/* - * 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; - -#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); -} - -/* Send a Set Disk Type command - does not seem to work with Aztech drives, behavior is completely indepen- - dent on which mode is set ??? -*/ -static int aztSetDiskType(int type) -{ - unsigned char data; - int retry; - -#ifdef AZT_DEBUG - 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); -} - - -/* 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; -/* int i; - - i = inb(STATUS_PORT) & AFL_STATUS; is STEN=0? ??? - if (!i) -*/ STEN_LOW; - if (aztTimeOutCount < AZT_TIMEOUT) { - st = inb(DATA_PORT) & 0xFF; - return st; - } else - RETURNM("aztStatus", -1); -} - -/* - * Get the drive status - */ -static int getAztStatus(void) -{ - int st; - - 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); -#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; - } - return st; -} - - -/* - * 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); - return 0; -} - -/* - * Subroutines to automatically close the door (tray) and - * lock it closed when the cd is mounted. Leave the tray - * locking as an option - */ -static void aztCloseDoor(void) -{ - aztSendCmd(ACMD_CLOSE); - STEN_LOW; - return; -} - -static void aztLockDoor(void) -{ -#if AZT_ALLOW_TRAY_LOCK - aztSendCmd(ACMD_LOCK); - STEN_LOW; -#endif - return; -} - -static void aztUnlockDoor(void) -{ -#if AZT_ALLOW_TRAY_LOCK - aztSendCmd(ACMD_UNLOCK); - STEN_LOW; -#endif - return; -} - -/* - * Read a value from the drive. Should return quickly, so a busy wait - * is used to avoid excessive rescheduling. The read command itself must - * be issued with aztSendCmd() directly before - */ -static int aztGetValue(unsigned char *result) -{ - int s; - - STEN_LOW; - if (aztTimeOutCount >= AZT_TIMEOUT) { - printk("aztcd: aztGetValue timeout\n"); - return -1; - } - s = inb(DATA_PORT) & 0xFF; - *result = (unsigned char) s; - return 0; -} - -/* - * Read the current Q-channel info. Also used for reading the - * table of contents. - */ -static int aztGetQChannelInfo(struct azt_Toc *qp) -{ - unsigned char notUsed; - int st; - -#ifdef AZT_DEBUG - 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); - } -#ifdef AZT_DEBUG - printk("aztcd: exiting aztGetQChannelInfo Time:%li\n", jiffies); -#endif - return 0; -} - -/* - * Read the table of contents (TOC) and TOC header if necessary - */ -static int aztUpdateToc(void) -{ - int st; - -#ifdef AZT_DEBUG - printk("aztcd: starting aztUpdateToc Time:%li\n", jiffies); -#endif - if (aztTocUpToDate) - return 0; - - if (aztGetDiskInfo() < 0) - return -EIO; - - 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; -#if AZT_MULTISESSION - 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; - } - - aztTocUpToDate = 1; -#ifdef AZT_DEBUG - printk("aztcd: exiting aztUpdateToc Time:%li\n", jiffies); -#endif - return 0; -} - - -/* Read the table of contents header, i.e. no. of tracks and start of first - * track - */ -static int aztGetDiskInfo(void) -{ - int limit; - unsigned char test; - struct azt_Toc qInfo; - -#ifdef AZT_DEBUG - 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 (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); -#endif - if (test != 0x0F) - return -1; - return 0; -} - -#if AZT_MULTISESSION -/* - * Get Multisession Disk Info - */ -static int aztGetMultiDiskInfo(void) -{ - int limit, k = 5; - unsigned char test; - struct azt_Toc qInfo; - -#ifdef AZT_DEBUG - 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 */ -#ifdef AZT_DEBUG_MULTISESSION - 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; - } - 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); -#endif - 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); -#endif - return 0; -} -#endif - -/* - * Read the table of contents (TOC) - */ -static int aztGetToc(int multi) -{ - int i, px; - int limit; - struct azt_Toc qInfo; - -#ifdef AZT_DEBUG - 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; - } - -/*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); -#endif - - return limit > 0 ? 0 : -1; -} - - -/*########################################################################## - Kernel Interface Functions - ########################################################################## -*/ - -#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; -} - -__setup("aztcd=", aztcd_setup); - -#endif /* !MODULE */ - -/* - * Checking if the media has been changed -*/ -static int check_aztcd_media_change(struct gendisk *disk) -{ - 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; - 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 cdrom_subchnl subchnl; - struct cdrom_volctrl volctrl; - void __user *argp = (void __user *)arg; - -#ifdef AZT_DEBUG - 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 */ - } - - 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); - 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; - - 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 */ - - 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; - /* restart the drive at the saved position. */ - i = aztPlay(&azt_Play); - if (i < 0) { - aztAudioStatus = CDROM_AUDIO_ERROR; - return -EIO; - } - aztAudioStatus = CDROM_AUDIO_PLAY; - break; - case CDROMMULTISESSION: /*multisession support -- experimental */ - { - struct cdrom_multisession ms; -#ifdef AZT_DEBUG - printk("aztcd ioctl MULTISESSION\n"); -#endif - if (copy_from_user(&ms, argp, - 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(argp, &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; - } - case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - if (copy_from_user(&ti, argp, sizeof ti)) - return -EFAULT; - if (ti.cdti_trk0 < DiskInfo.first - || ti.cdti_trk0 > DiskInfo.last - || ti.cdti_trk1 < ti.cdti_trk0) { - return -EINVAL; - } - if (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); -#endif - i = aztPlay(&azt_Play); - if (i < 0) { - aztAudioStatus = CDROM_AUDIO_ERROR; - return -EIO; - } - aztAudioStatus = CDROM_AUDIO_PLAY; - break; - 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, argp, sizeof msf)) - return -EFAULT; - /* convert to bcd */ - azt_bin2bcd(&msf.cdmsf_min0); - azt_bin2bcd(&msf.cdmsf_sec0); - azt_bin2bcd(&msf.cdmsf_frame0); - azt_bin2bcd(&msf.cdmsf_min1); - azt_bin2bcd(&msf.cdmsf_sec1); - azt_bin2bcd(&msf.cdmsf_frame1); - 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; -#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); -#endif - i = aztPlay(&azt_Play); - if (i < 0) { - aztAudioStatus = CDROM_AUDIO_ERROR; - return -EIO; - } - aztAudioStatus = CDROM_AUDIO_PLAY; - break; - - case CDROMREADTOCHDR: /* Read the table of contents header */ - tocHdr.cdth_trk0 = DiskInfo.first; - tocHdr.cdth_trk1 = DiskInfo.last; - if (copy_to_user(argp, &tocHdr, sizeof tocHdr)) - return -EFAULT; - break; - case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ - if (copy_from_user(&entry, argp, sizeof entry)) - return -EFAULT; - if ((!aztTocUpToDate) || aztDiskChanged) - aztUpdateToc(); - if (entry.cdte_track == CDROM_LEADOUT) - 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; - 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); - } else { - return -EINVAL; - } - if (copy_to_user(argp, &entry, sizeof entry)) - return -EFAULT; - break; - case CDROMSUBCHNL: /* Get subchannel info */ - if (copy_from_user - (&subchnl, argp, sizeof(struct cdrom_subchnl))) - return -EFAULT; - if (aztGetQChannelInfo(&qInfo) < 0) { -#ifdef AZT_DEBUG - printk - ("aztcd: exiting aztcd_ioctl - Error 3 - Command:%x\n", - cmd); -#endif - 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 (copy_to_user(argp, &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, argp, 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; - 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; - } - 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; -/*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, argp, 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(argp, &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(argp, &buf, CD_FRAMESIZE)) - return -EFAULT; - } - } - break; - case CDROMSEEK: /*seek msf address */ - if (copy_from_user(&msf, argp, sizeof msf)) - return -EFAULT; - /* convert to bcd */ - azt_bin2bcd(&msf.cdmsf_min0); - azt_bin2bcd(&msf.cdmsf_sec0); - azt_bin2bcd(&msf.cdmsf_frame0); - 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); - default: - return -EINVAL; - } -#ifdef AZT_DEBUG - printk("aztcd: exiting aztcd_ioctl Command:%x Time:%li\n", cmd, - jiffies); -#endif - return 0; -} - -/* - * Take care of the different block sizes between cdrom and Linux. - * 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); -#endif - if (!current_valid()) - return; - - 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; - } - } -} - -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); -#endif - if (DiskInfo.audio) { - printk("aztcd: Error, tried to mount an Audio CD\n"); - end_request(CURRENT, 0); - return; - } - azt_transfer_is_active = 1; - while (current_valid()) { - azt_transfer(); - if (CURRENT->nr_sectors == 0) { - end_request(CURRENT, 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(CURRENT, 0); - break; - } - } - azt_state = AZT_S_START; - AztTries = 5; - SET_TIMER(azt_poll, HZ / 100); - } - break; - } - } - 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); -#endif -} - - -static void azt_invalidate_buffers(void) -{ - int i; - -#ifdef AZT_DEBUG - 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; -} - -/* - * Open the device special file. Check that a disk is in. - */ -static int aztcd_open(struct inode *ip, struct file *fp) -{ - int st; - -#ifdef AZT_DEBUG - printk("aztcd: starting aztcd_open\n"); -#endif - - if (aztPresent == 0) - return -ENXIO; /* no hardware */ - - 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(); - -#ifdef AZT_DEBUG - printk("aztcd: exiting aztcd_open\n"); -#endif - return 0; - - err_out: - return -EIO; -} - - -/* - * On close, we flush all azt blocks from the buffer cache. - */ -static int aztcd_release(struct inode *inode, struct file *file) -{ -#ifdef AZT_DEBUG - printk("aztcd: executing aztcd_release\n"); - printk("inode: %p, device: %s file: %p\n", inode, - inode->i_bdev->bd_disk->disk_name, file); -#endif - if (!--azt_open_count) { - azt_invalidate_buffers(); - aztUnlockDoor(); - if (azt_auto_eject) - aztSendCmd(ACMD_EJECT); - CLEAR_TIMER; - } - return 0; -} - -static struct gendisk *azt_disk; - -/* - * Test for presence of drive and initialize it. Called at boot time. - */ - -static int __init aztcd_init(void) -{ - long int count, max_count; - unsigned char result[50]; - int st; - void* status = NULL; - int i = 0; - int ret = 0; - - if (azt_port == 0) { - printk(KERN_INFO "aztcd: no Aztech CD-ROM Initialization"); - return -EIO; - } - - printk(KERN_INFO "aztcd: AZTECH, ORCHID, OKANO, WEARNES, TXC, CyDROM " - "CD-ROM Driver\n"); - printk(KERN_INFO "aztcd: (C) 1994-98 W.Zimmermann\n"); - if (azt_port == -1) { - printk - ("aztcd: DriverVersion=%s For IDE/ATAPI-drives use ide-cd.c\n", - AZT_VERSION); - } else - printk - ("aztcd: DriverVersion=%s BaseAddress=0x%x For IDE/ATAPI-drives use ide-cd.c\n", - AZT_VERSION, azt_port); - printk(KERN_INFO "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 - - /* check for presence of drive */ - - if (azt_port == -1) { /* autoprobing for proprietary interface */ - for (i = 0; (azt_port_auto[i] != 0) && (i < 16); i++) { - azt_port = azt_port_auto[i]; - printk(KERN_INFO "aztcd: Autoprobing BaseAddress=0x%x" - "\n", azt_port); - /*proprietary interfaces need 4 bytes */ - if (!request_region(azt_port, 4, "aztcd")) { - 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) { /* OK drive found */ - break; - } - else { /* Drive not found on this port - try next one */ - release_region(azt_port, 4); - } - } - if ((i == 16) || (azt_port_auto[i] == 0)) { - printk(KERN_INFO "aztcd: no AZTECH CD-ROM drive found\n"); - return -EIO; - } - } else { /* no autoprobing */ - if ((azt_port == 0x1f0) || (azt_port == 0x170)) - status = request_region(azt_port, 8, "aztcd"); /*IDE-interfaces need 8 bytes */ - else - status = request_region(azt_port, 4, "aztcd"); /*proprietary interfaces need 4 bytes */ - if (!status) { - printk(KERN_WARNING "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(KERN_WARNING "aztcd: no AZTECH CD-ROM " - "drive found-Try boot parameter aztcd=" - "<BaseAddress>,0x79\n"); - ret = -EIO; - goto err_out; - } -#else - if (0) { - } -#endif - else { - printk(KERN_INFO "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(KERN_WARNING "aztcd: no AZTECH " - "CD-ROM drive found\n"); - ret = -EIO; - goto err_out; - } - - for (count = 0; count < AZT_TIMEOUT; - count++) - barrier(); /* Stop gcc 2.96 being smart */ - /* use udelay(), damnit -- AV */ - - if ((st = getAztStatus()) == -1) { - printk(KERN_WARNING "aztcd: Drive Status" - " Error Status=%x\n", st); - ret = -EIO; - goto err_out; - } -#ifdef AZT_DEBUG - printk(KERN_DEBUG "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; - } - } - } - - 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; - printk(KERN_INFO "aztcd: FirmwareVersion="); - 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"); - ret = -EIO; - goto err_out; - } - } - azt_disk = alloc_disk(1); - if (!azt_disk) - goto err_out; - - if (register_blkdev(MAJOR_NR, "aztcd")) { - ret = -EIO; - goto err_out2; - } - - azt_queue = blk_init_queue(do_aztcd_request, &aztSpin); - if (!azt_queue) { - ret = -ENOMEM; - goto err_out3; - } - - blk_queue_hardsect_size(azt_queue, 2048); - azt_disk->major = MAJOR_NR; - azt_disk->first_minor = 0; - azt_disk->fops = &azt_fops; - sprintf(azt_disk->disk_name, "aztcd"); - azt_disk->queue = azt_queue; - add_disk(azt_disk); - azt_invalidate_buffers(); - aztPresent = 1; - aztCloseDoor(); - return 0; -err_out3: - unregister_blkdev(MAJOR_NR, "aztcd"); -err_out2: - put_disk(azt_disk); -err_out: - 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 */ - return ret; - -} - -static void __exit aztcd_exit(void) -{ - del_gendisk(azt_disk); - put_disk(azt_disk); - if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) { - printk("What's that: can't unregister aztcd\n"); - return; - } - blk_cleanup_queue(azt_queue); - 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"); -} - -module_init(aztcd_init); -module_exit(aztcd_exit); - -/*########################################################################## - Aztcd State Machine: Controls Drive Operating State - ########################################################################## -*/ -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(); -#ifdef WARN_IF_READ_FAILURE - 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(CURRENT, 0); - AztTries = 5; - } - 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) { - - case AZT_S_IDLE: -#ifdef AZT_TEST3 - if (azt_state != azt_state_old) { - azt_state_old = azt_state; - printk("AZT_S_IDLE\n"); - } -#endif - return; - - case AZT_S_START: -#ifdef AZT_TEST3 - 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: -#ifdef AZT_TEST3 - 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(CURRENT, 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(CURRENT, 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(CURRENT, 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; - - break; - - - case AZT_S_READ: -#ifdef AZT_TEST3 - 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(CURRENT, 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(CURRENT, 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; - } -#endif - 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: -#ifdef AZT_TEST3 - if (azt_state != azt_state_old) { - azt_state_old = azt_state; - printk("AZT_S_DATA\n"); - } -#endif - - st = inb(STATUS_PORT) & AFL_STATUSorDATA; - - switch (st) { - - case AFL_DATA: -#ifdef AZT_TEST3 - 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(CURRENT, 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); - } -#endif - break; - - default: -#ifdef AZT_TEST3 - 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(CURRENT, 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]); -#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(CURRENT, 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"); - } -#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: -#ifdef AZT_TEST3 - 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(CURRENT, 0); - } - -#ifdef AZT_TEST3 - 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; - - 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); -} - - -/*########################################################################### - * Miscellaneous support functions - ########################################################################### -*/ -static void azt_hsg2msf(long hsg, struct msf *msf) -{ - hsg += 150; - msf->min = hsg / 4500; - hsg %= 4500; - 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"); -#endif - 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; -} - -static void azt_bin2bcd(unsigned char *p) -{ - int u, t; - - u = *p % 10; - t = *p / 10; - *p = u | (t << 4); -} - -static int azt_bcd2bin(unsigned char bcd) -{ - return (bcd >> 4) * 10 + (bcd & 0xF); -} - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_BLOCKDEV_MAJOR(AZTECH_CDROM_MAJOR); diff --git a/drivers/cdrom/aztcd.h b/drivers/cdrom/aztcd.h deleted file mode 100644 index 057501e3162..00000000000 --- a/drivers/cdrom/aztcd.h +++ /dev/null @@ -1,162 +0,0 @@ -/* $Id: aztcd.h,v 2.60 1997/11/29 09:51:22 root Exp root $ - * - * Definitions for a AztechCD268 CD-ROM interface - * Copyright (C) 1994-98 Werner Zimmermann - * - * based on Mitsumi CDROM driver by Martin Harriss - * - * 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. - * - * History: W.Zimmermann adaption to Aztech CD268-01A Version 1.3 - * October 1994 Email: Werner.Zimmermann@fht-esslingen.de - */ - -/* *** change this to set the I/O port address of your CD-ROM drive, - set to '-1', if you want autoprobing */ -#define AZT_BASE_ADDR -1 - -/* list of autoprobing addresses (not more than 15), last value must be 0x000 - Note: Autoprobing is only enabled, if AZT_BASE_ADDR is set to '-1' ! */ -#define AZT_BASE_AUTO { 0x320, 0x300, 0x310, 0x330, 0x000 } - -/* Uncomment this, if your CDROM is connected to a Soundwave32-soundcard - and configure AZT_BASE_ADDR and AZT_SW32_BASE_ADDR */ -/*#define AZT_SW32 1 -*/ - -#ifdef AZT_SW32 -#define AZT_SW32_BASE_ADDR 0x220 /*I/O port base address of your soundcard*/ -#endif - -/* Set this to 1, if you want your tray to be locked, set to 0 to prevent tray - from locking */ -#define AZT_ALLOW_TRAY_LOCK 1 - -/*Set this to 1 to allow auto-eject when unmounting a disk, set to 0, if you - don't want the auto-eject feature*/ -#define AZT_AUTO_EJECT 0 - -/*Set this to 1, if you want to use incompatible ioctls for reading in raw and - cooked mode */ -#define AZT_PRIVATE_IOCTLS 1 - -/*Set this to 1, if you want multisession support by the ISO fs. Even if you set - this value to '0' you can use multisession CDs. In that case the drive's firm- - ware will do the appropriate redirection automatically. The CD will then look - like a single session CD (but nevertheless all data may be read). Please read - chapter '5.1 Multisession support' in README.aztcd for details. Normally it's - uncritical to leave this setting untouched */ -#define AZT_MULTISESSION 1 - -/*Uncomment this, if you are using a linux kernel version prior to 2.1.0 */ -/*#define AZT_KERNEL_PRIOR_2_1 */ - -/*---------------------------------------------------------------------------*/ -/*-----nothing to be configured for normal applications below this line------*/ - - -/* Increase this if you get lots of timeouts; if you get kernel panic, replace - STEN_LOW_WAIT by STEN_LOW in the source code */ -#define AZT_STATUS_DELAY 400 /*for timer wait, STEN_LOW_WAIT*/ -#define AZT_TIMEOUT 8000000 /*for busy wait STEN_LOW, DTEN_LOW*/ -#define AZT_FAST_TIMEOUT 10000 /*for reading the version string*/ - -/* number of times to retry a command before giving up */ -#define AZT_RETRY_ATTEMPTS 3 - -/* port access macros */ -#define CMD_PORT azt_port -#define DATA_PORT azt_port -#define STATUS_PORT azt_port+1 -#define MODE_PORT azt_port+2 -#ifdef AZT_SW32 - #define AZT_SW32_INIT (unsigned int) (0xFF00 & (AZT_BASE_ADDR*16)) - #define AZT_SW32_CONFIG_REG AZT_SW32_BASE_ADDR+0x16 /*Soundwave32 Config. Register*/ - #define AZT_SW32_ID_REG AZT_SW32_BASE_ADDR+0x04 /*Soundwave32 ID Version Register*/ -#endif - -/* status bits */ -#define AST_CMD_CHECK 0x80 /* 1 = command error */ -#define AST_DOOR_OPEN 0x40 /* 1 = door is open */ -#define AST_NOT_READY 0x20 /* 1 = no disk in the drive */ -#define AST_DSK_CHG 0x02 /* 1 = disk removed or changed */ -#define AST_MODE 0x01 /* 0=MODE1, 1=MODE2 */ -#define AST_MODE_BITS 0x1C /* Mode Bits */ -#define AST_INITIAL 0x0C /* initial, only valid ... */ -#define AST_BUSY 0x04 /* now playing, only valid - in combination with mode - bits */ -/* flag bits */ -#define AFL_DATA 0x02 /* data available if low */ -#define AFL_STATUS 0x04 /* status available if low */ -#define AFL_OP_OK 0x01 /* OP_OK command correct*/ -#define AFL_PA_OK 0x02 /* PA_OK parameter correct*/ -#define AFL_OP_ERR 0x05 /* error in command*/ -#define AFL_PA_ERR 0x06 /* error in parameters*/ -#define POLLED 0x04 /* polled mode */ - -/* commands */ -#define ACMD_SOFT_RESET 0x10 /* reset drive */ -#define ACMD_PLAY_READ 0x20 /* read data track in cooked mode */ -#define ACMD_PLAY_READ_RAW 0x21 /* reading in raw mode*/ -#define ACMD_SEEK 0x30 /* seek msf address*/ -#define ACMD_SEEK_TO_LEADIN 0x31 /* seek to leadin track*/ -#define ACMD_GET_ERROR 0x40 /* get error code */ -#define ACMD_GET_STATUS 0x41 /* get status */ -#define ACMD_GET_Q_CHANNEL 0x50 /* read info from q channel */ -#define ACMD_EJECT 0x60 /* eject/open tray */ -#define ACMD_CLOSE 0x61 /* close tray */ -#define ACMD_LOCK 0x71 /* lock tray closed */ -#define ACMD_UNLOCK 0x72 /* unlock tray */ -#define ACMD_PAUSE 0x80 /* pause */ -#define ACMD_STOP 0x81 /* stop play */ -#define ACMD_PLAY_AUDIO 0x90 /* play audio track */ -#define ACMD_SET_VOLUME 0x93 /* set audio level */ -#define ACMD_GET_VERSION 0xA0 /* get firmware version */ -#define ACMD_SET_DISK_TYPE 0xA1 /* set disk data mode */ - -#define MAX_TRACKS 104 - -struct msf { - unsigned char min; - unsigned char sec; - unsigned char frame; -}; - -struct azt_Play_msf { - struct msf start; - struct msf end; -}; - -struct azt_DiskInfo { - unsigned char first; - unsigned char next; - unsigned char last; - struct msf diskLength; - struct msf firstTrack; - unsigned char multi; - struct msf nextSession; - struct msf lastSession; - unsigned char xa; - unsigned char audio; -}; - -struct azt_Toc { - unsigned char ctrl_addr; - unsigned char track; - unsigned char pointIndex; - struct msf trackTime; - struct msf diskTime; -}; diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 3625a05bc3d..aa5468f487b 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -302,7 +302,7 @@ module_param(lockdoor, bool, 0); module_param(check_media_type, bool, 0); module_param(mrw_format_restart, bool, 0); -static DEFINE_SPINLOCK(cdrom_lock); +static DEFINE_MUTEX(cdrom_mutex); static const char *mrw_format_status[] = { "not mrw", @@ -438,10 +438,10 @@ int register_cdrom(struct cdrom_device_info *cdi) cdo->generic_packet = cdrom_dummy_generic_packet; cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); - spin_lock(&cdrom_lock); + mutex_lock(&cdrom_mutex); cdi->next = topCdromPtr; topCdromPtr = cdi; - spin_unlock(&cdrom_lock); + mutex_unlock(&cdrom_mutex); return 0; } #undef ENSURE @@ -452,7 +452,7 @@ int unregister_cdrom(struct cdrom_device_info *unreg) cdinfo(CD_OPEN, "entering unregister_cdrom\n"); prev = NULL; - spin_lock(&cdrom_lock); + mutex_lock(&cdrom_mutex); cdi = topCdromPtr; while (cdi && cdi != unreg) { prev = cdi; @@ -460,7 +460,7 @@ int unregister_cdrom(struct cdrom_device_info *unreg) } if (cdi == NULL) { - spin_unlock(&cdrom_lock); + mutex_unlock(&cdrom_mutex); return -2; } if (prev) @@ -468,7 +468,7 @@ int unregister_cdrom(struct cdrom_device_info *unreg) else topCdromPtr = cdi->next; - spin_unlock(&cdrom_lock); + mutex_unlock(&cdrom_mutex); if (cdi->exit) cdi->exit(cdi); @@ -3289,103 +3289,137 @@ static struct cdrom_sysctl_settings { int check; /* check media type */ } cdrom_sysctl_settings; +enum cdrom_print_option { + CTL_NAME, + CTL_SPEED, + CTL_SLOTS, + CTL_CAPABILITY +}; + +static int cdrom_print_info(const char *header, int val, char *info, + int *pos, enum cdrom_print_option option) +{ + const int max_size = sizeof(cdrom_sysctl_settings.info); + struct cdrom_device_info *cdi; + int ret; + + ret = scnprintf(info + *pos, max_size - *pos, header); + if (!ret) + return 1; + + *pos += ret; + + for (cdi = topCdromPtr; cdi; cdi = cdi->next) { + switch (option) { + case CTL_NAME: + ret = scnprintf(info + *pos, max_size - *pos, + "\t%s", cdi->name); + break; + case CTL_SPEED: + ret = scnprintf(info + *pos, max_size - *pos, + "\t%d", cdi->speed); + break; + case CTL_SLOTS: + ret = scnprintf(info + *pos, max_size - *pos, + "\t%d", cdi->capacity); + break; + case CTL_CAPABILITY: + ret = scnprintf(info + *pos, max_size - *pos, + "\t%d", CDROM_CAN(val) != 0); + break; + default: + printk(KERN_INFO "cdrom: invalid option%d\n", option); + return 1; + } + if (!ret) + return 1; + *pos += ret; + } + + return 0; +} + static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos) { - int pos; - struct cdrom_device_info *cdi; + int pos; char *info = cdrom_sysctl_settings.info; + const int max_size = sizeof(cdrom_sysctl_settings.info); if (!*lenp || (*ppos && !write)) { *lenp = 0; return 0; } + mutex_lock(&cdrom_mutex); + pos = sprintf(info, "CD-ROM information, " VERSION "\n"); - pos += sprintf(info+pos, "\ndrive name:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%s", cdi->name); - - pos += sprintf(info+pos, "\ndrive speed:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", cdi->speed); - - pos += sprintf(info+pos, "\ndrive # of slots:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", cdi->capacity); - - pos += sprintf(info+pos, "\nCan close tray:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CLOSE_TRAY) != 0); - - pos += sprintf(info+pos, "\nCan open tray:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_OPEN_TRAY) != 0); - - pos += sprintf(info+pos, "\nCan lock tray:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_LOCK) != 0); - - pos += sprintf(info+pos, "\nCan change speed:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_SELECT_SPEED) != 0); - - pos += sprintf(info+pos, "\nCan select disk:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_SELECT_DISC) != 0); - - pos += sprintf(info+pos, "\nCan read multisession:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MULTI_SESSION) != 0); - - pos += sprintf(info+pos, "\nCan read MCN:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MCN) != 0); - - pos += sprintf(info+pos, "\nReports media changed:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MEDIA_CHANGED) != 0); - - pos += sprintf(info+pos, "\nCan play audio:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_PLAY_AUDIO) != 0); - - pos += sprintf(info+pos, "\nCan write CD-R:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CD_R) != 0); - - pos += sprintf(info+pos, "\nCan write CD-RW:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_CD_RW) != 0); - - pos += sprintf(info+pos, "\nCan read DVD:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD) != 0); - - pos += sprintf(info+pos, "\nCan write DVD-R:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_R) != 0); - - pos += sprintf(info+pos, "\nCan write DVD-RAM:"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_DVD_RAM) != 0); - - pos += sprintf(info+pos, "\nCan read MRW:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MRW) != 0); - - pos += sprintf(info+pos, "\nCan write MRW:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_MRW_W) != 0); - - pos += sprintf(info+pos, "\nCan write RAM:\t"); - for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) - pos += sprintf(info+pos, "\t%d", CDROM_CAN(CDC_RAM) != 0); - - strcpy(info+pos,"\n\n"); - - return proc_dostring(ctl, write, filp, buffer, lenp, ppos); + if (cdrom_print_info("\ndrive name:\t", 0, info, &pos, CTL_NAME)) + goto done; + if (cdrom_print_info("\ndrive speed:\t", 0, info, &pos, CTL_SPEED)) + goto done; + if (cdrom_print_info("\ndrive # of slots:", 0, info, &pos, CTL_SLOTS)) + goto done; + if (cdrom_print_info("\nCan close tray:\t", + CDC_CLOSE_TRAY, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan open tray:\t", + CDC_OPEN_TRAY, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan lock tray:\t", + CDC_LOCK, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan change speed:", + CDC_SELECT_SPEED, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan select disk:", + CDC_SELECT_DISC, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan read multisession:", + CDC_MULTI_SESSION, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan read MCN:\t", + CDC_MCN, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nReports media changed:", + CDC_MEDIA_CHANGED, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan play audio:\t", + CDC_PLAY_AUDIO, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write CD-R:\t", + CDC_CD_R, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write CD-RW:", + CDC_CD_RW, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan read DVD:\t", + CDC_DVD, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write DVD-R:", + CDC_DVD_R, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write DVD-RAM:", + CDC_DVD_RAM, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan read MRW:\t", + CDC_MRW, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write MRW:\t", + CDC_MRW_W, info, &pos, CTL_CAPABILITY)) + goto done; + if (cdrom_print_info("\nCan write RAM:\t", + CDC_RAM, info, &pos, CTL_CAPABILITY)) + goto done; + if (!scnprintf(info + pos, max_size - pos, "\n\n")) + goto done; +doit: + mutex_unlock(&cdrom_mutex); + return proc_dostring(ctl, write, filp, buffer, lenp, ppos); +done: + printk(KERN_INFO "cdrom: info buffer too small\n"); + goto doit; } /* Unfortunately, per device settings are not implemented through diff --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c deleted file mode 100644 index 2157c58755e..00000000000 --- a/drivers/cdrom/cdu31a.c +++ /dev/null @@ -1,3251 +0,0 @@ -/* -* Sony CDU-31A CDROM interface device driver. -* -* Corey Minyard (minyard@wf-rch.cirr.com) -* -* Colossians 3:17 -* -* See Documentation/cdrom/cdu31a for additional details about this driver. -* -* The Sony interface device driver handles Sony interface CDROM -* drives and provides a complete block-level interface as well as an -* ioctl() interface compatible with the Sun (as specified in -* include/linux/cdrom.h). With this interface, CDROMs can be -* accessed and standard audio CDs can be played back normally. -* -* WARNING - All autoprobes have been removed from the driver. -* You MUST configure the CDU31A via a LILO config -* at boot time or in lilo.conf. I have the -* following in my lilo.conf: -* -* append="cdu31a=0x1f88,0,PAS" -* -* The first number is the I/O base address of the -* card. The second is the interrupt (0 means none). - * The third should be "PAS" if on a Pro-Audio - * spectrum, or nothing if on something else. - * - * This interface is (unfortunately) a polled interface. This is - * because most Sony interfaces are set up with DMA and interrupts - * disables. Some (like mine) do not even have the capability to - * handle interrupts or DMA. For this reason you will see a lot of - * the following: - * - * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT; - * while (time_before(jiffies, retry_count) && (! <some condition to wait for)) - * { - * while (handle_sony_cd_attention()) - * ; - * - * sony_sleep(); - * } - * if (the condition not met) - * { - * return an error; - * } - * - * This ugly hack waits for something to happen, sleeping a little - * between every try. it also handles attentions, which are - * asynchronous events from the drive informing the driver that a disk - * has been inserted, removed, etc. - * - * NEWS FLASH - The driver now supports interrupts but they are - * turned off by default. Use of interrupts is highly encouraged, it - * cuts CPU usage down to a reasonable level. I had DMA in for a while - * but PC DMA is just too slow. Better to just insb() it. - * - * One thing about these drives: They talk in MSF (Minute Second Frame) format. - * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a - * disk. The funny thing is that these are sent to the drive in BCD, but the - * interface wants to see them in decimal. A lot of conversion goes on. - * - * DRIVER SPECIAL FEATURES - * ----------------------- - * - * This section describes features beyond the normal audio and CD-ROM - * functions of the drive. - * - * XA compatibility - * - * The driver should support XA disks for both the CDU31A and CDU33A. - * It does this transparently, the using program doesn't need to set it. - * - * Multi-Session - * - * A multi-session disk looks just like a normal disk to the user. - * Just mount one normally, and all the data should be there. - * A special thanks to Koen for help with this! - * - * Raw sector I/O - * - * Using the CDROMREADAUDIO it is possible to read raw audio and data - * tracks. Both operations return 2352 bytes per sector. On the data - * tracks, the first 12 bytes is not returned by the drive and the value - * of that data is indeterminate. - * - * - * Copyright (C) 1993 Corey Minyard - * - * 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. - * - * TODO: - * CDs with form1 and form2 sectors cause problems - * with current read-ahead strategy. - * - * Credits: - * Heiko Eissfeldt <heiko@colossus.escape.de> - * For finding abug in the return of the track numbers. - * TOC processing redone for proper multisession support. - * - * - * It probably a little late to be adding a history, but I guess I - * will start. - * - * 10/24/95 - Added support for disabling the eject button when the - * drive is open. Note that there is a small problem - * still here, if the eject button is pushed while the - * drive light is flashing, the drive will return a bad - * status and be reset. It recovers, though. - * - * 03/07/97 - Fixed a problem with timers. - * - * - * 18 Spetember 1997 -- Ported to Uniform CD-ROM driver by - * Heiko Eissfeldt <heiko@colossus.escape.de> with additional - * changes by Erik Andersen <andersee@debian.org> - * - * 24 January 1998 -- Removed the scd_disc_status() function, which was now - * just dead code left over from the port. - * Erik Andersen <andersee@debian.org> - * - * 16 July 1998 -- Drive donated to Erik Andersen by John Kodis - * <kodis@jagunet.com>. Work begun on fixing driver to - * work under 2.1.X. Added temporary extra printks - * which seem to slow it down enough to work. - * - * 9 November 1999 -- Make kernel-parameter implementation work with 2.3.x - * Removed init_module & cleanup_module in favor of - * module_init & module_exit. - * Torben Mathiasen <tmm@image.dk> - * - * 22 October 2004 -- Make the driver work in 2.6.X - * Added workaround to fix hard lockups on eject - * Fixed door locking problem after mounting empty drive - * Set double-speed drives to double speed by default - * Removed all readahead things - not needed anymore - * Ondrej Zary <rainbow@rainbow-software.org> -*/ - -#define DEBUG 1 - -#include <linux/major.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/hdreg.h> -#include <linux/genhd.h> -#include <linux/ioport.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/cdrom.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/dma.h> - -#include "cdu31a.h" - -#define MAJOR_NR CDU31A_CDROM_MAJOR -#include <linux/blkdev.h> - -#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10 - -#define PFX "CDU31A: " - -/* -** Edit the following data to change interrupts, DMA channels, etc. -** Default is polled and no DMA. DMA is not recommended for double-speed -** drives. -*/ -static struct { - unsigned short base; /* I/O Base Address */ - short int_num; /* Interrupt Number (-1 means scan for it, - 0 means don't use) */ -} cdu31a_addresses[] __initdata = { - {0} -}; - -static int handle_sony_cd_attention(void); -static int read_subcode(void); -static void sony_get_toc(void); -static int scd_spinup(void); -/*static int scd_open(struct inode *inode, struct file *filp);*/ -static int scd_open(struct cdrom_device_info *, int); -static void do_sony_cd_cmd(unsigned char cmd, - unsigned char *params, - unsigned int num_params, - unsigned char *result_buffer, - unsigned int *result_size); -static void size_to_buf(unsigned int size, unsigned char *buf); - -/* Parameters for the read-ahead. */ -static unsigned int sony_next_block; /* Next 512 byte block offset */ -static unsigned int sony_blocks_left = 0; /* Number of 512 byte blocks left - in the current read command. */ - - -/* The base I/O address of the Sony Interface. This is a variable (not a - #define) so it can be easily changed via some future ioctl() */ -static unsigned int cdu31a_port = 0; -module_param(cdu31a_port, uint, 0); - -/* - * The following are I/O addresses of the various registers for the drive. The - * comment for the base address also applies here. - */ -static volatile unsigned short sony_cd_cmd_reg; -static volatile unsigned short sony_cd_param_reg; -static volatile unsigned short sony_cd_write_reg; -static volatile unsigned short sony_cd_control_reg; -static volatile unsigned short sony_cd_status_reg; -static volatile unsigned short sony_cd_result_reg; -static volatile unsigned short sony_cd_read_reg; -static volatile unsigned short sony_cd_fifost_reg; - -static struct request_queue *cdu31a_queue; -static DEFINE_SPINLOCK(cdu31a_lock); /* queue lock */ - -static int sony_spun_up = 0; /* Has the drive been spun up? */ - -static int sony_speed = 0; /* Last wanted speed */ - -static int sony_xa_mode = 0; /* Is an XA disk in the drive - and the drive a CDU31A? */ - -static int sony_raw_data_mode = 1; /* 1 if data tracks, 0 if audio. - For raw data reads. */ - -static unsigned int sony_usage = 0; /* How many processes have the - drive open. */ - -static int sony_pas_init = 0; /* Initialize the Pro-Audio - Spectrum card? */ - -static struct s_sony_session_toc single_toc; /* Holds the - table of - contents. */ - -static struct s_all_sessions_toc sony_toc; /* entries gathered from all - sessions */ - -static int sony_toc_read = 0; /* Has the TOC been read for - the drive? */ - -static struct s_sony_subcode last_sony_subcode; /* Points to the last - subcode address read */ - -static DECLARE_MUTEX(sony_sem); /* Semaphore for drive hardware access */ - -static int is_double_speed = 0; /* does the drive support double speed ? */ - -static int is_auto_eject = 1; /* Door has been locked? 1=No/0=Yes */ - -/* - * The audio status uses the values from read subchannel data as specified - * in include/linux/cdrom.h. - */ -static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS; - -/* - * The following are a hack for pausing and resuming audio play. The drive - * does not work as I would expect it, if you stop it then start it again, - * the drive seeks back to the beginning and starts over. This holds the - * position during a pause so a resume can restart it. It uses the - * audio status variable above to tell if it is paused. - */ -static unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 }; -static unsigned volatile char final_pos_msf[3] = { 0, 0, 0 }; - -/* What IRQ is the drive using? 0 if none. */ -static int cdu31a_irq = 0; -module_param(cdu31a_irq, int, 0); - -/* The interrupt handler will wake this queue up when it gets an - interrupts. */ -static DECLARE_WAIT_QUEUE_HEAD(cdu31a_irq_wait); -static int irq_flag = 0; - -static int curr_control_reg = 0; /* Current value of the control register */ - -/* A disk changed variable. When a disk change is detected, it will - all be set to TRUE. As the upper layers ask for disk_changed status - it will be cleared. */ -static char disk_changed; - -/* This was readahead_buffer once... Now it's used only for audio reads */ -static char audio_buffer[CD_FRAMESIZE_RAW]; - -/* Used to time a short period to abort an operation after the - drive has been idle for a while. This keeps the light on - the drive from flashing for very long. */ -static struct timer_list cdu31a_abort_timer; - -/* Marks if the timeout has started an abort read. This is used - on entry to the drive to tell the code to read out the status - from the abort read. */ -static int abort_read_started = 0; - -/* - * Uniform cdrom interface function - * report back, if disc has changed from time of last request. - */ -static int scd_media_changed(struct cdrom_device_info *cdi, int disc_nr) -{ - int retval; - - retval = disk_changed; - disk_changed = 0; - - return retval; -} - -/* - * Uniform cdrom interface function - * report back, if drive is ready - */ -static int scd_drive_status(struct cdrom_device_info *cdi, int slot_nr) -{ - if (CDSL_CURRENT != slot_nr) - /* we have no changer support */ - return -EINVAL; - if (sony_spun_up) - return CDS_DISC_OK; - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - if (scd_spinup() == 0) - sony_spun_up = 1; - up(&sony_sem); - return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY; -} - -static inline void enable_interrupts(void) -{ - curr_control_reg |= (SONY_ATTN_INT_EN_BIT - | SONY_RES_RDY_INT_EN_BIT - | SONY_DATA_RDY_INT_EN_BIT); - outb(curr_control_reg, sony_cd_control_reg); -} - -static inline void disable_interrupts(void) -{ - curr_control_reg &= ~(SONY_ATTN_INT_EN_BIT - | SONY_RES_RDY_INT_EN_BIT - | SONY_DATA_RDY_INT_EN_BIT); - outb(curr_control_reg, sony_cd_control_reg); -} - -/* - * Wait a little while (used for polling the drive). If in initialization, - * setting a timeout doesn't work, so just loop for a while. - */ -static inline void sony_sleep(void) -{ - if (cdu31a_irq <= 0) { - yield(); - } else { /* Interrupt driven */ - DEFINE_WAIT(w); - int first = 1; - - while (1) { - prepare_to_wait(&cdu31a_irq_wait, &w, - TASK_INTERRUPTIBLE); - if (first) { - enable_interrupts(); - first = 0; - } - - if (irq_flag != 0) - break; - if (!signal_pending(current)) { - schedule(); - continue; - } else - disable_interrupts(); - break; - } - finish_wait(&cdu31a_irq_wait, &w); - irq_flag = 0; - } -} - - -/* - * The following are convenience routine to read various status and set - * various conditions in the drive. - */ -static inline int is_attention(void) -{ - return (inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0; -} - -static inline int is_busy(void) -{ - return (inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0; -} - -static inline int is_data_ready(void) -{ - return (inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0; -} - -static inline int is_data_requested(void) -{ - return (inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0; -} - -static inline int is_result_ready(void) -{ - return (inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0; -} - -static inline int is_param_write_rdy(void) -{ - return (inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0; -} - -static inline int is_result_reg_not_empty(void) -{ - return (inb(sony_cd_fifost_reg) & SONY_RES_REG_NOT_EMP_BIT) != 0; -} - -static inline void reset_drive(void) -{ - curr_control_reg = 0; - sony_toc_read = 0; - outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg); -} - -/* - * Uniform cdrom interface function - * reset drive and return when it is ready - */ -static int scd_reset(struct cdrom_device_info *cdi) -{ - unsigned long retry_count; - - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - reset_drive(); - - retry_count = jiffies + SONY_RESET_TIMEOUT; - while (time_before(jiffies, retry_count) && (!is_attention())) { - sony_sleep(); - } - - up(&sony_sem); - return 0; -} - -static inline void clear_attention(void) -{ - outb(curr_control_reg | SONY_ATTN_CLR_BIT, sony_cd_control_reg); -} - -static inline void clear_result_ready(void) -{ - outb(curr_control_reg | SONY_RES_RDY_CLR_BIT, sony_cd_control_reg); -} - -static inline void clear_data_ready(void) -{ - outb(curr_control_reg | SONY_DATA_RDY_CLR_BIT, - sony_cd_control_reg); -} - -static inline void clear_param_reg(void) -{ - outb(curr_control_reg | SONY_PARAM_CLR_BIT, sony_cd_control_reg); -} - -static inline unsigned char read_status_register(void) -{ - return inb(sony_cd_status_reg); -} - -static inline unsigned char read_result_register(void) -{ - return inb(sony_cd_result_reg); -} - -static inline unsigned char read_data_register(void) -{ - return inb(sony_cd_read_reg); -} - -static inline void write_param(unsigned char param) -{ - outb(param, sony_cd_param_reg); -} - -static inline void write_cmd(unsigned char cmd) -{ - outb(curr_control_reg | SONY_RES_RDY_INT_EN_BIT, - sony_cd_control_reg); - outb(cmd, sony_cd_cmd_reg); -} - -static irqreturn_t cdu31a_interrupt(int irq, void *dev_id) -{ - unsigned char val; - - if (abort_read_started) { - /* We might be waiting for an abort to finish. Don't - disable interrupts yet, though, because we handle - this one here. */ - /* Clear out the result registers. */ - while (is_result_reg_not_empty()) { - val = read_result_register(); - } - clear_data_ready(); - clear_result_ready(); - - /* Clear out the data */ - while (is_data_requested()) { - val = read_data_register(); - } - abort_read_started = 0; - - /* If something was waiting, wake it up now. */ - if (waitqueue_active(&cdu31a_irq_wait)) { - disable_interrupts(); - irq_flag = 1; - wake_up_interruptible(&cdu31a_irq_wait); - } - } else if (waitqueue_active(&cdu31a_irq_wait)) { - disable_interrupts(); - irq_flag = 1; - wake_up_interruptible(&cdu31a_irq_wait); - } else { - disable_interrupts(); - printk(KERN_NOTICE PFX - "Got an interrupt but nothing was waiting\n"); - } - return IRQ_HANDLED; -} - -/* - * give more verbose error messages - */ -static unsigned char *translate_error(unsigned char err_code) -{ - static unsigned char errbuf[80]; - - switch (err_code) { - case 0x10: return "illegal command "; - case 0x11: return "illegal parameter "; - - case 0x20: return "not loaded "; - case 0x21: return "no disc "; - case 0x22: return "not spinning "; - case 0x23: return "spinning "; - case 0x25: return "spindle servo "; - case 0x26: return "focus servo "; - case 0x29: return "eject mechanism "; - case 0x2a: return "audio playing "; - case 0x2c: return "emergency eject "; - - case 0x30: return "focus "; - case 0x31: return "frame sync "; - case 0x32: return "subcode address "; - case 0x33: return "block sync "; - case 0x34: return "header address "; - - case 0x40: return "illegal track read "; - case 0x41: return "mode 0 read "; - case 0x42: return "illegal mode read "; - case 0x43: return "illegal block size read "; - case 0x44: return "mode read "; - case 0x45: return "form read "; - case 0x46: return "leadout read "; - case 0x47: return "buffer overrun "; - - case 0x53: return "unrecoverable CIRC "; - case 0x57: return "unrecoverable LECC "; - - case 0x60: return "no TOC "; - case 0x61: return "invalid subcode data "; - case 0x63: return "focus on TOC read "; - case 0x64: return "frame sync on TOC read "; - case 0x65: return "TOC data "; - - case 0x70: return "hardware failure "; - case 0x91: return "leadin "; - case 0x92: return "leadout "; - case 0x93: return "data track "; - } - sprintf(errbuf, "unknown 0x%02x ", err_code); - return errbuf; -} - -/* - * Set the drive parameters so the drive will auto-spin-up when a - * disk is inserted. - */ -static void set_drive_params(int want_doublespeed) -{ - unsigned char res_reg[12]; - unsigned int res_size; - unsigned char params[3]; - - - params[0] = SONY_SD_AUTO_SPIN_DOWN_TIME; - params[1] = 0x00; /* Never spin down the drive. */ - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, 2, res_reg, &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_NOTICE PFX - "Unable to set spin-down time: 0x%2.2x\n", res_reg[1]); - } - - params[0] = SONY_SD_MECH_CONTROL; - params[1] = SONY_AUTO_SPIN_UP_BIT; /* Set auto spin up */ - - if (is_auto_eject) - params[1] |= SONY_AUTO_EJECT_BIT; - - if (is_double_speed && want_doublespeed) { - params[1] |= SONY_DOUBLE_SPEED_BIT; /* Set the drive to double speed if - possible */ - } - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, 2, res_reg, &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_NOTICE PFX "Unable to set mechanical " - "parameters: 0x%2.2x\n", res_reg[1]); - } -} - -/* - * Uniform cdrom interface function - * select reading speed for data access - */ -static int scd_select_speed(struct cdrom_device_info *cdi, int speed) -{ - if (speed == 0) - sony_speed = 1; - else - sony_speed = speed - 1; - - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - set_drive_params(sony_speed); - up(&sony_sem); - return 0; -} - -/* - * Uniform cdrom interface function - * lock or unlock eject button - */ -static int scd_lock_door(struct cdrom_device_info *cdi, int lock) -{ - if (lock == 0) { - is_auto_eject = 1; - } else { - is_auto_eject = 0; - } - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - set_drive_params(sony_speed); - up(&sony_sem); - return 0; -} - -/* - * This code will reset the drive and attempt to restore sane parameters. - */ -static void restart_on_error(void) -{ - unsigned char res_reg[12]; - unsigned int res_size; - unsigned long retry_count; - - - printk(KERN_NOTICE PFX "Resetting drive on error\n"); - reset_drive(); - retry_count = jiffies + SONY_RESET_TIMEOUT; - while (time_before(jiffies, retry_count) && (!is_attention())) { - sony_sleep(); - } - set_drive_params(sony_speed); - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_NOTICE PFX "Unable to spin up drive: 0x%2.2x\n", - res_reg[1]); - } - - msleep(2000); - - sony_get_toc(); -} - -/* - * This routine writes data to the parameter register. Since this should - * happen fairly fast, it is polled with no OS waits between. - */ -static int write_params(unsigned char *params, int num_params) -{ - unsigned int retry_count; - - - retry_count = SONY_READY_RETRIES; - while ((retry_count > 0) && (!is_param_write_rdy())) { - retry_count--; - } - if (!is_param_write_rdy()) { - return -EIO; - } - - while (num_params > 0) { - write_param(*params); - params++; - num_params--; - } - - return 0; -} - - -/* - * The following reads data from the command result register. It is a - * fairly complex routine, all status info flows back through this - * interface. The algorithm is stolen directly from the flowcharts in - * the drive manual. - */ -static void -get_result(unsigned char *result_buffer, unsigned int *result_size) -{ - unsigned char a, b; - int i; - unsigned long retry_count; - - - while (handle_sony_cd_attention()); - /* Wait for the result data to be ready */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while (time_before(jiffies, retry_count) - && (is_busy() || (!(is_result_ready())))) { - sony_sleep(); - - while (handle_sony_cd_attention()); - } - if (is_busy() || (!(is_result_ready()))) { - pr_debug(PFX "timeout out %d\n", __LINE__); - result_buffer[0] = 0x20; - result_buffer[1] = SONY_TIMEOUT_OP_ERR; - *result_size = 2; - return; - } - - /* - * Get the first two bytes. This determines what else needs - * to be done. - */ - clear_result_ready(); - a = read_result_register(); - *result_buffer = a; - result_buffer++; - - /* Check for block error status result. */ - if ((a & 0xf0) == 0x50) { - *result_size = 1; - return; - } - - b = read_result_register(); - *result_buffer = b; - result_buffer++; - *result_size = 2; - - /* - * 0x20 means an error occurred. Byte 2 will have the error code. - * Otherwise, the command succeeded, byte 2 will have the count of - * how many more status bytes are coming. - * - * The result register can be read 10 bytes at a time, a wait for - * result ready to be asserted must be done between every 10 bytes. - */ - if ((a & 0xf0) != 0x20) { - if (b > 8) { - for (i = 0; i < 8; i++) { - *result_buffer = read_result_register(); - result_buffer++; - (*result_size)++; - } - b = b - 8; - - while (b > 10) { - retry_count = SONY_READY_RETRIES; - while ((retry_count > 0) - && (!is_result_ready())) { - retry_count--; - } - if (!is_result_ready()) { - pr_debug(PFX "timeout out %d\n", - __LINE__); - result_buffer[0] = 0x20; - result_buffer[1] = - SONY_TIMEOUT_OP_ERR; - *result_size = 2; - return; - } - - clear_result_ready(); - - for (i = 0; i < 10; i++) { - *result_buffer = - read_result_register(); - result_buffer++; - (*result_size)++; - } - b = b - 10; - } - - if (b > 0) { - retry_count = SONY_READY_RETRIES; - while ((retry_count > 0) - && (!is_result_ready())) { - retry_count--; - } - if (!is_result_ready()) { - pr_debug(PFX "timeout out %d\n", - __LINE__); - result_buffer[0] = 0x20; - result_buffer[1] = - SONY_TIMEOUT_OP_ERR; - *result_size = 2; - return; - } - } - } - - while (b > 0) { - *result_buffer = read_result_register(); - result_buffer++; - (*result_size)++; - b--; - } - } -} - -/* - * Do a command that does not involve data transfer. This routine must - * be re-entrant from the same task to support being called from the - * data operation code when an error occurs. - */ -static void -do_sony_cd_cmd(unsigned char cmd, - unsigned char *params, - unsigned int num_params, - unsigned char *result_buffer, unsigned int *result_size) -{ - unsigned long retry_count; - int num_retries = 0; - -retry_cd_operation: - - while (handle_sony_cd_attention()); - - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while (time_before(jiffies, retry_count) && (is_busy())) { - sony_sleep(); - - while (handle_sony_cd_attention()); - } - if (is_busy()) { - pr_debug(PFX "timeout out %d\n", __LINE__); - result_buffer[0] = 0x20; - result_buffer[1] = SONY_TIMEOUT_OP_ERR; - *result_size = 2; - } else { - clear_result_ready(); - clear_param_reg(); - - write_params(params, num_params); - write_cmd(cmd); - - get_result(result_buffer, result_size); - } - - if (((result_buffer[0] & 0xf0) == 0x20) - && (num_retries < MAX_CDU31A_RETRIES)) { - num_retries++; - msleep(100); - goto retry_cd_operation; - } -} - - -/* - * Handle an attention from the drive. This will return 1 if it found one - * or 0 if not (if one is found, the caller might want to call again). - * - * This routine counts the number of consecutive times it is called - * (since this is always called from a while loop until it returns - * a 0), and returns a 0 if it happens too many times. This will help - * prevent a lockup. - */ -static int handle_sony_cd_attention(void) -{ - unsigned char atten_code; - static int num_consecutive_attentions = 0; - volatile int val; - - -#if 0 - pr_debug(PFX "Entering %s\n", __FUNCTION__); -#endif - if (is_attention()) { - if (num_consecutive_attentions > - CDU31A_MAX_CONSECUTIVE_ATTENTIONS) { - printk(KERN_NOTICE PFX "Too many consecutive " - "attentions: %d\n", num_consecutive_attentions); - num_consecutive_attentions = 0; - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, - __LINE__); - return 0; - } - - clear_attention(); - atten_code = read_result_register(); - - switch (atten_code) { - /* Someone changed the CD. Mark it as changed */ - case SONY_MECH_LOADED_ATTN: - disk_changed = 1; - sony_toc_read = 0; - sony_audio_status = CDROM_AUDIO_NO_STATUS; - sony_blocks_left = 0; - break; - - case SONY_SPIN_DOWN_COMPLETE_ATTN: - /* Mark the disk as spun down. */ - sony_spun_up = 0; - break; - - case SONY_AUDIO_PLAY_DONE_ATTN: - sony_audio_status = CDROM_AUDIO_COMPLETED; - read_subcode(); - break; - - case SONY_EJECT_PUSHED_ATTN: - if (is_auto_eject) { - sony_audio_status = CDROM_AUDIO_INVALID; - } - break; - - case SONY_LEAD_IN_ERR_ATTN: - case SONY_LEAD_OUT_ERR_ATTN: - case SONY_DATA_TRACK_ERR_ATTN: - case SONY_AUDIO_PLAYBACK_ERR_ATTN: - sony_audio_status = CDROM_AUDIO_ERROR; - break; - } - - num_consecutive_attentions++; - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__); - return 1; - } else if (abort_read_started) { - while (is_result_reg_not_empty()) { - val = read_result_register(); - } - clear_data_ready(); - clear_result_ready(); - /* Clear out the data */ - while (is_data_requested()) { - val = read_data_register(); - } - abort_read_started = 0; - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__); - return 1; - } - - num_consecutive_attentions = 0; -#if 0 - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__); -#endif - return 0; -} - - -/* Convert from an integer 0-99 to BCD */ -static inline unsigned int int_to_bcd(unsigned int val) -{ - int retval; - - - retval = (val / 10) << 4; - retval = retval | val % 10; - return retval; -} - - -/* Convert from BCD to an integer from 0-99 */ -static unsigned int bcd_to_int(unsigned int bcd) -{ - return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f); -} - - -/* - * Convert a logical sector value (like the OS would want to use for - * a block device) to an MSF format. - */ -static void log_to_msf(unsigned int log, unsigned char *msf) -{ - log = log + LOG_START_OFFSET; - msf[0] = int_to_bcd(log / 4500); - log = log % 4500; - msf[1] = int_to_bcd(log / 75); - msf[2] = int_to_bcd(log % 75); -} - - -/* - * Convert an MSF format to a logical sector. - */ -static unsigned int msf_to_log(unsigned char *msf) -{ - unsigned int log; - - - log = msf[2]; - log += msf[1] * 75; - log += msf[0] * 4500; - log = log - LOG_START_OFFSET; - - return log; -} - - -/* - * Take in integer size value and put it into a buffer like - * the drive would want to see a number-of-sector value. - */ -static void size_to_buf(unsigned int size, unsigned char *buf) -{ - buf[0] = size / 65536; - size = size % 65536; - buf[1] = size / 256; - buf[2] = size % 256; -} - -/* Starts a read operation. Returns 0 on success and 1 on failure. - The read operation used here allows multiple sequential sectors - to be read and status returned for each sector. The driver will - read the output one at a time as the requests come and abort the - operation if the requested sector is not the next one from the - drive. */ -static int -start_request(unsigned int sector, unsigned int nsect) -{ - unsigned char params[6]; - unsigned long retry_count; - - - pr_debug(PFX "Entering %s\n", __FUNCTION__); - log_to_msf(sector, params); - size_to_buf(nsect, ¶ms[3]); - - /* - * Clear any outstanding attentions and wait for the drive to - * complete any pending operations. - */ - while (handle_sony_cd_attention()); - - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while (time_before(jiffies, retry_count) && (is_busy())) { - sony_sleep(); - - while (handle_sony_cd_attention()); - } - - if (is_busy()) { - printk(KERN_NOTICE PFX "Timeout while waiting " - "to issue command\n"); - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__); - return 1; - } else { - /* Issue the command */ - clear_result_ready(); - clear_param_reg(); - - write_params(params, 6); - write_cmd(SONY_READ_BLKERR_STAT_CMD); - - sony_blocks_left = nsect * 4; - sony_next_block = sector * 4; - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__); - return 0; - } - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__); -} - -/* Abort a pending read operation. Clear all the drive status variables. */ -static void abort_read(void) -{ - unsigned char result_reg[2]; - int result_size; - volatile int val; - - - do_sony_cd_cmd(SONY_ABORT_CMD, NULL, 0, result_reg, &result_size); - if ((result_reg[0] & 0xf0) == 0x20) { - printk(KERN_ERR PFX "Aborting read, %s error\n", - translate_error(result_reg[1])); - } - - while (is_result_reg_not_empty()) { - val = read_result_register(); - } - clear_data_ready(); - clear_result_ready(); - /* Clear out the data */ - while (is_data_requested()) { - val = read_data_register(); - } - - sony_blocks_left = 0; -} - -/* Called when the timer times out. This will abort the - pending read operation. */ -static void handle_abort_timeout(unsigned long data) -{ - pr_debug(PFX "Entering %s\n", __FUNCTION__); - /* If it is in use, ignore it. */ - if (down_trylock(&sony_sem) == 0) { - /* We can't use abort_read(), because it will sleep - or schedule in the timer interrupt. Just start - the operation, finish it on the next access to - the drive. */ - clear_result_ready(); - clear_param_reg(); - write_cmd(SONY_ABORT_CMD); - - sony_blocks_left = 0; - abort_read_started = 1; - up(&sony_sem); - } - pr_debug(PFX "Leaving %s\n", __FUNCTION__); -} - -/* Actually get one sector of data from the drive. */ -static void -input_data_sector(char *buffer) -{ - pr_debug(PFX "Entering %s\n", __FUNCTION__); - - /* If an XA disk on a CDU31A, skip the first 12 bytes of data from - the disk. The real data is after that. We can use audio_buffer. */ - if (sony_xa_mode) - insb(sony_cd_read_reg, audio_buffer, CD_XA_HEAD); - - clear_data_ready(); - - insb(sony_cd_read_reg, buffer, 2048); - - /* If an XA disk, we have to clear out the rest of the unused - error correction data. We can use audio_buffer for that. */ - if (sony_xa_mode) - insb(sony_cd_read_reg, audio_buffer, CD_XA_TAIL); - - pr_debug(PFX "Leaving %s\n", __FUNCTION__); -} - -/* read data from the drive. Note the nsect must be <= 4. */ -static void -read_data_block(char *buffer, - unsigned int block, - unsigned int nblocks, - unsigned char res_reg[], int *res_size) -{ - unsigned long retry_count; - - pr_debug(PFX "Entering %s\n", __FUNCTION__); - - res_reg[0] = 0; - res_reg[1] = 0; - *res_size = 0; - - /* Wait for the drive to tell us we have something */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while (time_before(jiffies, retry_count) && !(is_data_ready())) { - while (handle_sony_cd_attention()); - - sony_sleep(); - } - if (!(is_data_ready())) { - if (is_result_ready()) { - get_result(res_reg, res_size); - if ((res_reg[0] & 0xf0) != 0x20) { - printk(KERN_NOTICE PFX "Got result that should" - " have been error: %d\n", res_reg[0]); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - abort_read(); - } else { - pr_debug(PFX "timeout out %d\n", __LINE__); - res_reg[0] = 0x20; - res_reg[1] = SONY_TIMEOUT_OP_ERR; - *res_size = 2; - abort_read(); - } - } else { - input_data_sector(buffer); - sony_blocks_left -= nblocks; - sony_next_block += nblocks; - - /* Wait for the status from the drive. */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while (time_before(jiffies, retry_count) - && !(is_result_ready())) { - while (handle_sony_cd_attention()); - - sony_sleep(); - } - - if (!is_result_ready()) { - pr_debug(PFX "timeout out %d\n", __LINE__); - res_reg[0] = 0x20; - res_reg[1] = SONY_TIMEOUT_OP_ERR; - *res_size = 2; - abort_read(); - } else { - get_result(res_reg, res_size); - - /* If we got a buffer status, handle that. */ - if ((res_reg[0] & 0xf0) == 0x50) { - - if ((res_reg[0] == - SONY_NO_CIRC_ERR_BLK_STAT) - || (res_reg[0] == - SONY_NO_LECC_ERR_BLK_STAT) - || (res_reg[0] == - SONY_RECOV_LECC_ERR_BLK_STAT)) { - /* nothing here */ - } else { - printk(KERN_ERR PFX "Data block " - "error: 0x%x\n", res_reg[0]); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - - /* Final transfer is done for read command, get final result. */ - if (sony_blocks_left == 0) { - get_result(res_reg, res_size); - } - } else if ((res_reg[0] & 0xf0) != 0x20) { - /* The drive gave me bad status, I don't know what to do. - Reset the driver and return an error. */ - printk(KERN_ERR PFX "Invalid block " - "status: 0x%x\n", res_reg[0]); - restart_on_error(); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - } - } - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__); -} - - -/* - * The OS calls this to perform a read or write operation to the drive. - * Write obviously fail. Reads to a read ahead of sony_buffer_size - * bytes to help speed operations. This especially helps since the OS - * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most - * data access on a CD is done sequentially, this saves a lot of operations. - */ -static void do_cdu31a_request(request_queue_t * q) -{ - struct request *req; - int block, nblock, num_retries; - unsigned char res_reg[12]; - unsigned int res_size; - - pr_debug(PFX "Entering %s\n", __FUNCTION__); - - spin_unlock_irq(q->queue_lock); - if (down_interruptible(&sony_sem)) { - spin_lock_irq(q->queue_lock); - return; - } - - /* Get drive status before doing anything. */ - while (handle_sony_cd_attention()); - - /* Make sure we have a valid TOC. */ - sony_get_toc(); - - - /* Make sure the timer is cancelled. */ - del_timer(&cdu31a_abort_timer); - - while (1) { - /* - * The beginning here is stolen from the hard disk driver. I hope - * it's right. - */ - req = elv_next_request(q); - if (!req) - goto end_do_cdu31a_request; - - if (!sony_spun_up) - scd_spinup(); - - block = req->sector; - nblock = req->nr_sectors; - pr_debug(PFX "request at block %d, length %d blocks\n", - block, nblock); - if (!sony_toc_read) { - printk(KERN_NOTICE PFX "TOC not read\n"); - end_request(req, 0); - continue; - } - - /* WTF??? */ - if (!blk_fs_request(req)) { - end_request(req, 0); - continue; - } - if (rq_data_dir(req) == WRITE) { - end_request(req, 0); - continue; - } - - /* - * If the block address is invalid or the request goes beyond the end of - * the media, return an error. - */ - if (((block + nblock) / 4) >= sony_toc.lead_out_start_lba) { - printk(KERN_NOTICE PFX "Request past end of media\n"); - end_request(req, 0); - continue; - } - - if (nblock > 4) - nblock = 4; - num_retries = 0; - - try_read_again: - while (handle_sony_cd_attention()); - - if (!sony_toc_read) { - printk(KERN_NOTICE PFX "TOC not read\n"); - end_request(req, 0); - continue; - } - - /* If no data is left to be read from the drive, start the - next request. */ - if (sony_blocks_left == 0) { - if (start_request(block / 4, nblock / 4)) { - end_request(req, 0); - continue; - } - } - /* If the requested block is not the next one waiting in - the driver, abort the current operation and start a - new one. */ - else if (block != sony_next_block) { - pr_debug(PFX "Read for block %d, expected %d\n", - block, sony_next_block); - abort_read(); - if (!sony_toc_read) { - printk(KERN_NOTICE PFX "TOC not read\n"); - end_request(req, 0); - continue; - } - if (start_request(block / 4, nblock / 4)) { - printk(KERN_NOTICE PFX "start request failed\n"); - end_request(req, 0); - continue; - } - } - - read_data_block(req->buffer, block, nblock, res_reg, &res_size); - - if (res_reg[0] != 0x20) { - if (!end_that_request_first(req, 1, nblock)) { - spin_lock_irq(q->queue_lock); - blkdev_dequeue_request(req); - end_that_request_last(req, 1); - spin_unlock_irq(q->queue_lock); - } - continue; - } - - if (num_retries > MAX_CDU31A_RETRIES) { - end_request(req, 0); - continue; - } - - num_retries++; - if (res_reg[1] == SONY_NOT_SPIN_ERR) { - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, - &res_size); - } else { - printk(KERN_NOTICE PFX "%s error for block %d, nblock %d\n", - translate_error(res_reg[1]), block, nblock); - } - goto try_read_again; - } - end_do_cdu31a_request: -#if 0 - /* After finished, cancel any pending operations. */ - abort_read(); -#else - /* Start a timer to time out after a while to disable - the read. */ - cdu31a_abort_timer.expires = jiffies + 2 * HZ; /* Wait 2 seconds */ - add_timer(&cdu31a_abort_timer); -#endif - - up(&sony_sem); - spin_lock_irq(q->queue_lock); - pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__); -} - - -/* - * Read the table of contents from the drive and set up TOC if - * successful. - */ -static void sony_get_toc(void) -{ - unsigned char res_reg[2]; - unsigned int res_size; - unsigned char parms[1]; - int session; - int num_spin_ups; - int totaltracks = 0; - int mint = 99; - int maxt = 0; - - pr_debug(PFX "Entering %s\n", __FUNCTION__); - - num_spin_ups = 0; - if (!sony_toc_read) { - respinup_on_gettoc: - /* Ignore the result, since it might error if spinning already. */ - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, - &res_size); - - do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, - &res_size); - - /* The drive sometimes returns error 0. I don't know why, but ignore - it. It seems to mean the drive has already done the operation. */ - if ((res_size < 2) - || ((res_reg[0] != 0) && (res_reg[1] != 0))) { - /* If the drive is already playing, it's ok. */ - if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) - || (res_reg[1] == 0)) { - goto gettoc_drive_spinning; - } - - /* If the drive says it is not spun up (even though we just did it!) - then retry the operation at least a few times. */ - if ((res_reg[1] == SONY_NOT_SPIN_ERR) - && (num_spin_ups < MAX_CDU31A_RETRIES)) { - num_spin_ups++; - goto respinup_on_gettoc; - } - - printk("cdu31a: Error reading TOC: %x %s\n", - res_reg[0], translate_error(res_reg[1])); - return; - } - - gettoc_drive_spinning: - - /* The idea here is we keep asking for sessions until the command - fails. Then we know what the last valid session on the disk is. - No need to check session 0, since session 0 is the same as session - 1; the command returns different information if you give it 0. - */ -#if DEBUG - memset(&sony_toc, 0x0e, sizeof(sony_toc)); - memset(&single_toc, 0x0f, sizeof(single_toc)); -#endif - session = 1; - while (1) { -/* This seems to slow things down enough to make it work. This - * appears to be a problem in do_sony_cd_cmd. This printk seems - * to address the symptoms... -Erik */ - pr_debug(PFX "Trying session %d\n", session); - parms[0] = session; - do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD, - parms, 1, res_reg, &res_size); - - pr_debug(PFX "%2.2x %2.2x\n", res_reg[0], res_reg[1]); - - if ((res_size < 2) - || ((res_reg[0] & 0xf0) == 0x20)) { - /* An error reading the TOC, this must be past the last session. */ - if (session == 1) - printk - ("Yikes! Couldn't read any sessions!"); - break; - } - pr_debug(PFX "Reading session %d\n", session); - - parms[0] = session; - do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD, - parms, - 1, - (unsigned char *) &single_toc, - &res_size); - if ((res_size < 2) - || ((single_toc.exec_status[0] & 0xf0) == - 0x20)) { - printk(KERN_ERR PFX "Error reading " - "session %d: %x %s\n", - session, single_toc.exec_status[0], - translate_error(single_toc. - exec_status[1])); - /* An error reading the TOC. Return without sony_toc_read - set. */ - return; - } - pr_debug(PFX "add0 %01x, con0 %01x, poi0 %02x, " - "1st trk %d, dsktyp %x, dum0 %x\n", - single_toc.address0, single_toc.control0, - single_toc.point0, - bcd_to_int(single_toc.first_track_num), - single_toc.disk_type, single_toc.dummy0); - pr_debug(PFX "add1 %01x, con1 %01x, poi1 %02x, " - "lst trk %d, dummy1 %x, dum2 %x\n", - single_toc.address1, single_toc.control1, - single_toc.point1, - bcd_to_int(single_toc.last_track_num), - single_toc.dummy1, single_toc.dummy2); - pr_debug(PFX "add2 %01x, con2 %01x, poi2 %02x " - "leadout start min %d, sec %d, frame %d\n", - single_toc.address2, single_toc.control2, - single_toc.point2, - bcd_to_int(single_toc.lead_out_start_msf[0]), - bcd_to_int(single_toc.lead_out_start_msf[1]), - bcd_to_int(single_toc.lead_out_start_msf[2])); - if (res_size > 18 && single_toc.pointb0 > 0xaf) - pr_debug(PFX "addb0 %01x, conb0 %01x, poib0 %02x, nextsession min %d, sec %d, frame %d\n" - "#mode5_ptrs %02d, max_start_outer_leadout_msf min %d, sec %d, frame %d\n", - single_toc.addressb0, - single_toc.controlb0, - single_toc.pointb0, - bcd_to_int(single_toc. - next_poss_prog_area_msf - [0]), - bcd_to_int(single_toc. - next_poss_prog_area_msf - [1]), - bcd_to_int(single_toc. - next_poss_prog_area_msf - [2]), - single_toc.num_mode_5_pointers, - bcd_to_int(single_toc. - max_start_outer_leadout_msf - [0]), - bcd_to_int(single_toc. - max_start_outer_leadout_msf - [1]), - bcd_to_int(single_toc. - max_start_outer_leadout_msf - [2])); - if (res_size > 27 && single_toc.pointb1 > 0xaf) - pr_debug(PFX "addb1 %01x, conb1 %01x, poib1 %02x, %x %x %x %x #skipint_ptrs %d, #skiptrkassign %d %x\n", - single_toc.addressb1, - single_toc.controlb1, - single_toc.pointb1, - single_toc.dummyb0_1[0], - single_toc.dummyb0_1[1], - single_toc.dummyb0_1[2], - single_toc.dummyb0_1[3], - single_toc.num_skip_interval_pointers, - single_toc.num_skip_track_assignments, - single_toc.dummyb0_2); - if (res_size > 36 && single_toc.pointb2 > 0xaf) - pr_debug(PFX "addb2 %01x, conb2 %01x, poib2 %02x, %02x %02x %02x %02x %02x %02x %02x\n", - single_toc.addressb2, - single_toc.controlb2, - single_toc.pointb2, - single_toc.tracksb2[0], - single_toc.tracksb2[1], - single_toc.tracksb2[2], - single_toc.tracksb2[3], - single_toc.tracksb2[4], - single_toc.tracksb2[5], - single_toc.tracksb2[6]); - if (res_size > 45 && single_toc.pointb3 > 0xaf) - pr_debug(PFX "addb3 %01x, conb3 %01x, poib3 %02x, %02x %02x %02x %02x %02x %02x %02x\n", - single_toc.addressb3, - single_toc.controlb3, - single_toc.pointb3, - single_toc.tracksb3[0], - single_toc.tracksb3[1], - single_toc.tracksb3[2], - single_toc.tracksb3[3], - single_toc.tracksb3[4], - single_toc.tracksb3[5], - single_toc.tracksb3[6]); - if (res_size > 54 && single_toc.pointb4 > 0xaf) - pr_debug(PFX "addb4 %01x, conb4 %01x, poib4 %02x, %02x %02x %02x %02x %02x %02x %02x\n", - single_toc.addressb4, - single_toc.controlb4, - single_toc.pointb4, - single_toc.tracksb4[0], - single_toc.tracksb4[1], - single_toc.tracksb4[2], - single_toc.tracksb4[3], - single_toc.tracksb4[4], - single_toc.tracksb4[5], - single_toc.tracksb4[6]); - if (res_size > 63 && single_toc.pointc0 > 0xaf) - pr_debug(PFX "addc0 %01x, conc0 %01x, poic0 %02x, %02x %02x %02x %02x %02x %02x %02x\n", - single_toc.addressc0, - single_toc.controlc0, - single_toc.pointc0, - single_toc.dummyc0[0], - single_toc.dummyc0[1], - single_toc.dummyc0[2], - single_toc.dummyc0[3], - single_toc.dummyc0[4], - single_toc.dummyc0[5], - single_toc.dummyc0[6]); -#undef DEBUG -#define DEBUG 0 - - sony_toc.lead_out_start_msf[0] = - bcd_to_int(single_toc.lead_out_start_msf[0]); - sony_toc.lead_out_start_msf[1] = - bcd_to_int(single_toc.lead_out_start_msf[1]); - sony_toc.lead_out_start_msf[2] = - bcd_to_int(single_toc.lead_out_start_msf[2]); - sony_toc.lead_out_start_lba = - single_toc.lead_out_start_lba = - msf_to_log(sony_toc.lead_out_start_msf); - - /* For points that do not exist, move the data over them - to the right location. */ - if (single_toc.pointb0 != 0xb0) { - memmove(((char *) &single_toc) + 27, - ((char *) &single_toc) + 18, - res_size - 18); - res_size += 9; - } else if (res_size > 18) { - sony_toc.lead_out_start_msf[0] = - bcd_to_int(single_toc. - max_start_outer_leadout_msf - [0]); - sony_toc.lead_out_start_msf[1] = - bcd_to_int(single_toc. - max_start_outer_leadout_msf - [1]); - sony_toc.lead_out_start_msf[2] = - bcd_to_int(single_toc. - max_start_outer_leadout_msf - [2]); - sony_toc.lead_out_start_lba = - msf_to_log(sony_toc. - lead_out_start_msf); - } - if (single_toc.pointb1 != 0xb1) { - memmove(((char *) &single_toc) + 36, - ((char *) &single_toc) + 27, - res_size - 27); - res_size += 9; - } - if (single_toc.pointb2 != 0xb2) { - memmove(((char *) &single_toc) + 45, - ((char *) &single_toc) + 36, - res_size - 36); - res_size += 9; - } - if (single_toc.pointb3 != 0xb3) { - memmove(((char *) &single_toc) + 54, - ((char *) &single_toc) + 45, - res_size - 45); - res_size += 9; - } - if (single_toc.pointb4 != 0xb4) { - memmove(((char *) &single_toc) + 63, - ((char *) &single_toc) + 54, - res_size - 54); - res_size += 9; - } - if (single_toc.pointc0 != 0xc0) { - memmove(((char *) &single_toc) + 72, - ((char *) &single_toc) + 63, - res_size - 63); - res_size += 9; - } -#if DEBUG - printk(PRINT_INFO PFX "start track lba %u, " - "leadout start lba %u\n", - single_toc.start_track_lba, - single_toc.lead_out_start_lba); - { - int i; - for (i = 0; - i < - 1 + - bcd_to_int(single_toc.last_track_num) - - - bcd_to_int(single_toc. - first_track_num); i++) { - printk(KERN_INFO PFX "trk %02d: add 0x%01x, con 0x%01x, track %02d, start min %02d, sec %02d, frame %02d\n", - i, - single_toc.tracks[i].address, - single_toc.tracks[i].control, - bcd_to_int(single_toc. - tracks[i].track), - bcd_to_int(single_toc. - tracks[i]. - track_start_msf - [0]), - bcd_to_int(single_toc. - tracks[i]. - track_start_msf - [1]), - bcd_to_int(single_toc. - tracks[i]. - track_start_msf - [2])); - if (mint > - bcd_to_int(single_toc. - tracks[i].track)) - mint = - bcd_to_int(single_toc. - tracks[i]. - track); - if (maxt < - bcd_to_int(single_toc. - tracks[i].track)) - maxt = - bcd_to_int(single_toc. - tracks[i]. - track); - } - printk(KERN_INFO PFX "min track number %d, " - "max track number %d\n", - mint, maxt); - } -#endif - - /* prepare a special table of contents for a CD-I disc. They don't have one. */ - if (single_toc.disk_type == 0x10 && - single_toc.first_track_num == 2 && - single_toc.last_track_num == 2 /* CD-I */ ) { - sony_toc.tracks[totaltracks].address = 1; - sony_toc.tracks[totaltracks].control = 4; /* force data tracks */ - sony_toc.tracks[totaltracks].track = 1; - sony_toc.tracks[totaltracks]. - track_start_msf[0] = 0; - sony_toc.tracks[totaltracks]. - track_start_msf[1] = 2; - sony_toc.tracks[totaltracks]. - track_start_msf[2] = 0; - mint = maxt = 1; - totaltracks++; - } else - /* gather track entries from this session */ - { - int i; - for (i = 0; - i < - 1 + - bcd_to_int(single_toc.last_track_num) - - - bcd_to_int(single_toc. - first_track_num); - i++, totaltracks++) { - sony_toc.tracks[totaltracks]. - address = - single_toc.tracks[i].address; - sony_toc.tracks[totaltracks]. - control = - single_toc.tracks[i].control; - sony_toc.tracks[totaltracks]. - track = - bcd_to_int(single_toc. - tracks[i].track); - sony_toc.tracks[totaltracks]. - track_start_msf[0] = - bcd_to_int(single_toc. - tracks[i]. - track_start_msf[0]); - sony_toc.tracks[totaltracks]. - track_start_msf[1] = - bcd_to_int(single_toc. - tracks[i]. - track_start_msf[1]); - sony_toc.tracks[totaltracks]. - track_start_msf[2] = - bcd_to_int(single_toc. - tracks[i]. - track_start_msf[2]); - if (i == 0) - single_toc. - start_track_lba = - msf_to_log(sony_toc. - tracks - [totaltracks]. - track_start_msf); - if (mint > - sony_toc.tracks[totaltracks]. - track) - mint = - sony_toc. - tracks[totaltracks]. - track; - if (maxt < - sony_toc.tracks[totaltracks]. - track) - maxt = - sony_toc. - tracks[totaltracks]. - track; - } - } - sony_toc.first_track_num = mint; - sony_toc.last_track_num = maxt; - /* Disk type of last session wins. For example: - CD-Extra has disk type 0 for the first session, so - a dumb HiFi CD player thinks it is a plain audio CD. - We are interested in the disk type of the last session, - which is 0x20 (XA) for CD-Extra, so we can access the - data track ... */ - sony_toc.disk_type = single_toc.disk_type; - sony_toc.sessions = session; - - /* don't believe everything :-) */ - if (session == 1) - single_toc.start_track_lba = 0; - sony_toc.start_track_lba = - single_toc.start_track_lba; - - if (session > 1 && single_toc.pointb0 == 0xb0 && - sony_toc.lead_out_start_lba == - single_toc.lead_out_start_lba) { - break; - } - - /* Let's not get carried away... */ - if (session > 40) { - printk(KERN_NOTICE PFX "too many sessions: " - "%d\n", session); - break; - } - session++; - } - sony_toc.track_entries = totaltracks; - /* add one entry for the LAST track with track number CDROM_LEADOUT */ - sony_toc.tracks[totaltracks].address = single_toc.address2; - sony_toc.tracks[totaltracks].control = single_toc.control2; - sony_toc.tracks[totaltracks].track = CDROM_LEADOUT; - sony_toc.tracks[totaltracks].track_start_msf[0] = - sony_toc.lead_out_start_msf[0]; - sony_toc.tracks[totaltracks].track_start_msf[1] = - sony_toc.lead_out_start_msf[1]; - sony_toc.tracks[totaltracks].track_start_msf[2] = - sony_toc.lead_out_start_msf[2]; - - sony_toc_read = 1; - - pr_debug(PFX "Disk session %d, start track: %d, " - "stop track: %d\n", - session, single_toc.start_track_lba, - single_toc.lead_out_start_lba); - } - pr_debug(PFX "Leaving %s\n", __FUNCTION__); -} - - -/* - * Uniform cdrom interface function - * return multisession offset and sector information - */ -static int scd_get_last_session(struct cdrom_device_info *cdi, - struct cdrom_multisession *ms_info) -{ - if (ms_info == NULL) - return 1; - - if (!sony_toc_read) { - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - sony_get_toc(); - up(&sony_sem); - } - - ms_info->addr_format = CDROM_LBA; - ms_info->addr.lba = sony_toc.start_track_lba; - ms_info->xa_flag = sony_toc.disk_type == SONY_XA_DISK_TYPE || - sony_toc.disk_type == 0x10 /* CDI */ ; - - return 0; -} - -/* - * Search for a specific track in the table of contents. - */ -static int find_track(int track) -{ - int i; - - for (i = 0; i <= sony_toc.track_entries; i++) { - if (sony_toc.tracks[i].track == track) { - return i; - } - } - - return -1; -} - - -/* - * Read the subcode and put it in last_sony_subcode for future use. - */ -static int read_subcode(void) -{ - unsigned int res_size; - - - do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD, - NULL, - 0, (unsigned char *) &last_sony_subcode, &res_size); - if ((res_size < 2) - || ((last_sony_subcode.exec_status[0] & 0xf0) == 0x20)) { - printk(KERN_ERR PFX "Sony CDROM error %s (read_subcode)\n", - translate_error(last_sony_subcode.exec_status[1])); - return -EIO; - } - - last_sony_subcode.track_num = - bcd_to_int(last_sony_subcode.track_num); - last_sony_subcode.index_num = - bcd_to_int(last_sony_subcode.index_num); - last_sony_subcode.abs_msf[0] = - bcd_to_int(last_sony_subcode.abs_msf[0]); - last_sony_subcode.abs_msf[1] = - bcd_to_int(last_sony_subcode.abs_msf[1]); - last_sony_subcode.abs_msf[2] = - bcd_to_int(last_sony_subcode.abs_msf[2]); - - last_sony_subcode.rel_msf[0] = - bcd_to_int(last_sony_subcode.rel_msf[0]); - last_sony_subcode.rel_msf[1] = - bcd_to_int(last_sony_subcode.rel_msf[1]); - last_sony_subcode.rel_msf[2] = - bcd_to_int(last_sony_subcode.rel_msf[2]); - return 0; -} - -/* - * Uniform cdrom interface function - * return the media catalog number found on some older audio cds - */ -static int -scd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) -{ - unsigned char resbuffer[2 + 14]; - unsigned char *mcnp = mcn->medium_catalog_number; - unsigned char *resp = resbuffer + 3; - unsigned int res_size; - - memset(mcn->medium_catalog_number, 0, 14); - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - do_sony_cd_cmd(SONY_REQ_UPC_EAN_CMD, - NULL, 0, resbuffer, &res_size); - up(&sony_sem); - if ((res_size < 2) || ((resbuffer[0] & 0xf0) == 0x20)); - else { - /* packed bcd to single ASCII digits */ - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - } - *mcnp = '\0'; - return 0; -} - - -/* - * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If - * the drive is playing, the subchannel needs to be read (since it would be - * changing). If the drive is paused or completed, the subcode information has - * already been stored, just use that. The ioctl call wants things in decimal - * (not BCD), so all the conversions are done. - */ -static int sony_get_subchnl_info(struct cdrom_subchnl *schi) -{ - /* Get attention stuff */ - while (handle_sony_cd_attention()); - - sony_get_toc(); - if (!sony_toc_read) { - return -EIO; - } - - switch (sony_audio_status) { - case CDROM_AUDIO_NO_STATUS: - case CDROM_AUDIO_PLAY: - if (read_subcode() < 0) { - return -EIO; - } - break; - - case CDROM_AUDIO_PAUSED: - case CDROM_AUDIO_COMPLETED: - break; - -#if 0 - case CDROM_AUDIO_NO_STATUS: - schi->cdsc_audiostatus = sony_audio_status; - return 0; - break; -#endif - case CDROM_AUDIO_INVALID: - case CDROM_AUDIO_ERROR: - default: - return -EIO; - } - - schi->cdsc_audiostatus = sony_audio_status; - schi->cdsc_adr = last_sony_subcode.address; - schi->cdsc_ctrl = last_sony_subcode.control; - schi->cdsc_trk = last_sony_subcode.track_num; - schi->cdsc_ind = last_sony_subcode.index_num; - if (schi->cdsc_format == CDROM_MSF) { - schi->cdsc_absaddr.msf.minute = - last_sony_subcode.abs_msf[0]; - schi->cdsc_absaddr.msf.second = - last_sony_subcode.abs_msf[1]; - schi->cdsc_absaddr.msf.frame = - last_sony_subcode.abs_msf[2]; - - schi->cdsc_reladdr.msf.minute = - last_sony_subcode.rel_msf[0]; - schi->cdsc_reladdr.msf.second = - last_sony_subcode.rel_msf[1]; - schi->cdsc_reladdr.msf.frame = - last_sony_subcode.rel_msf[2]; - } else if (schi->cdsc_format == CDROM_LBA) { - schi->cdsc_absaddr.lba = - msf_to_log(last_sony_subcode.abs_msf); - schi->cdsc_reladdr.lba = - msf_to_log(last_sony_subcode.rel_msf); - } - - return 0; -} - -/* Get audio data from the drive. This is fairly complex because I - am looking for status and data at the same time, but if I get status - then I just look for data. I need to get the status immediately so - the switch from audio to data tracks will happen quickly. */ -static void -read_audio_data(char *buffer, unsigned char res_reg[], int *res_size) -{ - unsigned long retry_count; - int result_read; - - - res_reg[0] = 0; - res_reg[1] = 0; - *res_size = 0; - result_read = 0; - - /* Wait for the drive to tell us we have something */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - continue_read_audio_wait: - while (time_before(jiffies, retry_count) && !(is_data_ready()) - && !(is_result_ready() || result_read)) { - while (handle_sony_cd_attention()); - - sony_sleep(); - } - if (!(is_data_ready())) { - if (is_result_ready() && !result_read) { - get_result(res_reg, res_size); - - /* Read block status and continue waiting for data. */ - if ((res_reg[0] & 0xf0) == 0x50) { - result_read = 1; - goto continue_read_audio_wait; - } - /* Invalid data from the drive. Shut down the operation. */ - else if ((res_reg[0] & 0xf0) != 0x20) { - printk(KERN_WARNING PFX "Got result that " - "should have been error: %d\n", - res_reg[0]); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - abort_read(); - } else { - pr_debug(PFX "timeout out %d\n", __LINE__); - res_reg[0] = 0x20; - res_reg[1] = SONY_TIMEOUT_OP_ERR; - *res_size = 2; - abort_read(); - } - } else { - clear_data_ready(); - - /* If data block, then get 2340 bytes offset by 12. */ - if (sony_raw_data_mode) { - insb(sony_cd_read_reg, buffer + CD_XA_HEAD, - CD_FRAMESIZE_RAW1); - } else { - /* Audio gets the whole 2352 bytes. */ - insb(sony_cd_read_reg, buffer, CD_FRAMESIZE_RAW); - } - - /* If I haven't already gotten the result, get it now. */ - if (!result_read) { - /* Wait for the drive to tell us we have something */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while (time_before(jiffies, retry_count) - && !(is_result_ready())) { - while (handle_sony_cd_attention()); - - sony_sleep(); - } - - if (!is_result_ready()) { - pr_debug(PFX "timeout out %d\n", __LINE__); - res_reg[0] = 0x20; - res_reg[1] = SONY_TIMEOUT_OP_ERR; - *res_size = 2; - abort_read(); - return; - } else { - get_result(res_reg, res_size); - } - } - - if ((res_reg[0] & 0xf0) == 0x50) { - if ((res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT) - || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT) - || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT) - || (res_reg[0] == SONY_NO_ERR_DETECTION_STAT)) { - /* Ok, nothing to do. */ - } else { - printk(KERN_ERR PFX "Data block error: 0x%x\n", - res_reg[0]); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - } else if ((res_reg[0] & 0xf0) != 0x20) { - /* The drive gave me bad status, I don't know what to do. - Reset the driver and return an error. */ - printk(KERN_NOTICE PFX "Invalid block status: 0x%x\n", - res_reg[0]); - restart_on_error(); - res_reg[0] = 0x20; - res_reg[1] = SONY_BAD_DATA_ERR; - *res_size = 2; - } - } -} - -/* Perform a raw data read. This will automatically detect the - track type and read the proper data (audio or data). */ -static int read_audio(struct cdrom_read_audio *ra) -{ - int retval; - unsigned char params[2]; - unsigned char res_reg[12]; - unsigned int res_size; - unsigned int cframe; - - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - if (!sony_spun_up) - scd_spinup(); - - /* Set the drive to do raw operations. */ - params[0] = SONY_SD_DECODE_PARAM; - params[1] = 0x06 | sony_raw_data_mode; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, 2, res_reg, &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_ERR PFX "Unable to set decode params: 0x%2.2x\n", - res_reg[1]); - retval = -EIO; - goto out_up; - } - - /* From here down, we have to goto exit_read_audio instead of returning - because the drive parameters have to be set back to data before - return. */ - - retval = 0; - if (start_request(ra->addr.lba, ra->nframes)) { - retval = -EIO; - goto exit_read_audio; - } - - /* For every requested frame. */ - cframe = 0; - while (cframe < ra->nframes) { - read_audio_data(audio_buffer, res_reg, &res_size); - if ((res_reg[0] & 0xf0) == 0x20) { - if (res_reg[1] == SONY_BAD_DATA_ERR) { - printk(KERN_ERR PFX "Data error on audio " - "sector %d\n", - ra->addr.lba + cframe); - } else if (res_reg[1] == SONY_ILL_TRACK_R_ERR) { - /* Illegal track type, change track types and start over. */ - sony_raw_data_mode = - (sony_raw_data_mode) ? 0 : 1; - - /* Set the drive mode. */ - params[0] = SONY_SD_DECODE_PARAM; - params[1] = 0x06 | sony_raw_data_mode; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, - 2, res_reg, &res_size); - if ((res_size < 2) - || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_ERR PFX "Unable to set " - "decode params: 0x%2.2x\n", - res_reg[1]); - retval = -EIO; - goto exit_read_audio; - } - - /* Restart the request on the current frame. */ - if (start_request - (ra->addr.lba + cframe, - ra->nframes - cframe)) { - retval = -EIO; - goto exit_read_audio; - } - - /* Don't go back to the top because don't want to get into - and infinite loop. A lot of code gets duplicated, but - that's no big deal, I don't guess. */ - read_audio_data(audio_buffer, res_reg, - &res_size); - if ((res_reg[0] & 0xf0) == 0x20) { - if (res_reg[1] == - SONY_BAD_DATA_ERR) { - printk(KERN_ERR PFX "Data error" - " on audio sector %d\n", - ra->addr.lba + - cframe); - } else { - printk(KERN_ERR PFX "Error reading audio data on sector %d: %s\n", - ra->addr.lba + cframe, - translate_error - (res_reg[1])); - retval = -EIO; - goto exit_read_audio; - } - } else if (copy_to_user(ra->buf + - (CD_FRAMESIZE_RAW - * cframe), - audio_buffer, - CD_FRAMESIZE_RAW)) { - retval = -EFAULT; - goto exit_read_audio; - } - } else { - printk(KERN_ERR PFX "Error reading audio " - "data on sector %d: %s\n", - ra->addr.lba + cframe, - translate_error(res_reg[1])); - retval = -EIO; - goto exit_read_audio; - } - } else if (copy_to_user(ra->buf + (CD_FRAMESIZE_RAW * cframe), - (char *)audio_buffer, - CD_FRAMESIZE_RAW)) { - retval = -EFAULT; - goto exit_read_audio; - } - - cframe++; - } - - get_result(res_reg, &res_size); - if ((res_reg[0] & 0xf0) == 0x20) { - printk(KERN_ERR PFX "Error return from audio read: %s\n", - translate_error(res_reg[1])); - retval = -EIO; - goto exit_read_audio; - } - - exit_read_audio: - - /* Set the drive mode back to the proper one for the disk. */ - params[0] = SONY_SD_DECODE_PARAM; - if (!sony_xa_mode) { - params[1] = 0x0f; - } else { - params[1] = 0x07; - } - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, 2, res_reg, &res_size); - if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_ERR PFX "Unable to reset decode params: 0x%2.2x\n", - res_reg[1]); - retval = -EIO; - } - - out_up: - up(&sony_sem); - - return retval; -} - -static int -do_sony_cd_cmd_chk(const char *name, - unsigned char cmd, - unsigned char *params, - unsigned int num_params, - unsigned char *result_buffer, unsigned int *result_size) -{ - do_sony_cd_cmd(cmd, params, num_params, result_buffer, - result_size); - if ((*result_size < 2) || ((result_buffer[0] & 0xf0) == 0x20)) { - printk(KERN_ERR PFX "Error %s (CDROM%s)\n", - translate_error(result_buffer[1]), name); - return -EIO; - } - return 0; -} - -/* - * Uniform cdrom interface function - * open the tray - */ -static int scd_tray_move(struct cdrom_device_info *cdi, int position) -{ - int retval; - - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - if (position == 1 /* open tray */ ) { - unsigned char res_reg[12]; - unsigned int res_size; - - do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, - &res_size); - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, - &res_size); - - sony_audio_status = CDROM_AUDIO_INVALID; - retval = do_sony_cd_cmd_chk("EJECT", SONY_EJECT_CMD, NULL, 0, - res_reg, &res_size); - } else { - if (0 == scd_spinup()) - sony_spun_up = 1; - retval = 0; - } - up(&sony_sem); - return retval; -} - -/* - * The big ugly ioctl handler. - */ -static int scd_audio_ioctl(struct cdrom_device_info *cdi, - unsigned int cmd, void *arg) -{ - unsigned char res_reg[12]; - unsigned int res_size; - unsigned char params[7]; - int i, retval; - - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - switch (cmd) { - case CDROMSTART: /* Spin up the drive */ - retval = do_sony_cd_cmd_chk("START", SONY_SPIN_UP_CMD, NULL, - 0, res_reg, &res_size); - break; - - case CDROMSTOP: /* Spin down the drive */ - do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, - &res_size); - - /* - * Spin the drive down, ignoring the error if the disk was - * already not spinning. - */ - sony_audio_status = CDROM_AUDIO_NO_STATUS; - retval = do_sony_cd_cmd_chk("STOP", SONY_SPIN_DOWN_CMD, NULL, - 0, res_reg, &res_size); - break; - - case CDROMPAUSE: /* Pause the drive */ - if (do_sony_cd_cmd_chk - ("PAUSE", SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, - &res_size)) { - retval = -EIO; - break; - } - /* Get the current position and save it for resuming */ - if (read_subcode() < 0) { - retval = -EIO; - break; - } - cur_pos_msf[0] = last_sony_subcode.abs_msf[0]; - cur_pos_msf[1] = last_sony_subcode.abs_msf[1]; - cur_pos_msf[2] = last_sony_subcode.abs_msf[2]; - sony_audio_status = CDROM_AUDIO_PAUSED; - retval = 0; - break; - - case CDROMRESUME: /* Start the drive after being paused */ - if (sony_audio_status != CDROM_AUDIO_PAUSED) { - retval = -EINVAL; - break; - } - - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, - &res_size); - - /* Start the drive at the saved position. */ - params[1] = int_to_bcd(cur_pos_msf[0]); - params[2] = int_to_bcd(cur_pos_msf[1]); - params[3] = int_to_bcd(cur_pos_msf[2]); - params[4] = int_to_bcd(final_pos_msf[0]); - params[5] = int_to_bcd(final_pos_msf[1]); - params[6] = int_to_bcd(final_pos_msf[2]); - params[0] = 0x03; - if (do_sony_cd_cmd_chk - ("RESUME", SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, - &res_size) < 0) { - retval = -EIO; - break; - } - sony_audio_status = CDROM_AUDIO_PLAY; - retval = 0; - break; - - case CDROMPLAYMSF: /* Play starting at the given MSF address. */ - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, - &res_size); - - /* The parameters are given in int, must be converted */ - for (i = 1; i < 7; i++) { - params[i] = - int_to_bcd(((unsigned char *) arg)[i - 1]); - } - params[0] = 0x03; - if (do_sony_cd_cmd_chk - ("PLAYMSF", SONY_AUDIO_PLAYBACK_CMD, params, 7, - res_reg, &res_size) < 0) { - retval = -EIO; - break; - } - - /* Save the final position for pauses and resumes */ - final_pos_msf[0] = bcd_to_int(params[4]); - final_pos_msf[1] = bcd_to_int(params[5]); - final_pos_msf[2] = bcd_to_int(params[6]); - sony_audio_status = CDROM_AUDIO_PLAY; - retval = 0; - break; - - case CDROMREADTOCHDR: /* Read the table of contents header */ - { - struct cdrom_tochdr *hdr; - - sony_get_toc(); - if (!sony_toc_read) { - retval = -EIO; - break; - } - - hdr = (struct cdrom_tochdr *) arg; - hdr->cdth_trk0 = sony_toc.first_track_num; - hdr->cdth_trk1 = sony_toc.last_track_num; - } - retval = 0; - break; - - case CDROMREADTOCENTRY: /* Read a given table of contents entry */ - { - struct cdrom_tocentry *entry; - int track_idx; - unsigned char *msf_val = NULL; - - sony_get_toc(); - if (!sony_toc_read) { - retval = -EIO; - break; - } - - entry = (struct cdrom_tocentry *) arg; - - track_idx = find_track(entry->cdte_track); - if (track_idx < 0) { - retval = -EINVAL; - break; - } - - entry->cdte_adr = - sony_toc.tracks[track_idx].address; - entry->cdte_ctrl = - sony_toc.tracks[track_idx].control; - msf_val = - sony_toc.tracks[track_idx].track_start_msf; - - /* Logical buffer address or MSF format requested? */ - if (entry->cdte_format == CDROM_LBA) { - entry->cdte_addr.lba = msf_to_log(msf_val); - } else if (entry->cdte_format == CDROM_MSF) { - entry->cdte_addr.msf.minute = *msf_val; - entry->cdte_addr.msf.second = - *(msf_val + 1); - entry->cdte_addr.msf.frame = - *(msf_val + 2); - } - } - retval = 0; - break; - - case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - { - struct cdrom_ti *ti = (struct cdrom_ti *) arg; - int track_idx; - - sony_get_toc(); - if (!sony_toc_read) { - retval = -EIO; - break; - } - - if ((ti->cdti_trk0 < sony_toc.first_track_num) - || (ti->cdti_trk0 > sony_toc.last_track_num) - || (ti->cdti_trk1 < ti->cdti_trk0)) { - retval = -EINVAL; - break; - } - - track_idx = find_track(ti->cdti_trk0); - if (track_idx < 0) { - retval = -EINVAL; - break; - } - params[1] = - int_to_bcd(sony_toc.tracks[track_idx]. - track_start_msf[0]); - params[2] = - int_to_bcd(sony_toc.tracks[track_idx]. - track_start_msf[1]); - params[3] = - int_to_bcd(sony_toc.tracks[track_idx]. - track_start_msf[2]); - - /* - * If we want to stop after the last track, use the lead-out - * MSF to do that. - */ - if (ti->cdti_trk1 >= sony_toc.last_track_num) { - track_idx = find_track(CDROM_LEADOUT); - } else { - track_idx = find_track(ti->cdti_trk1 + 1); - } - if (track_idx < 0) { - retval = -EINVAL; - break; - } - params[4] = - int_to_bcd(sony_toc.tracks[track_idx]. - track_start_msf[0]); - params[5] = - int_to_bcd(sony_toc.tracks[track_idx]. - track_start_msf[1]); - params[6] = - int_to_bcd(sony_toc.tracks[track_idx]. - track_start_msf[2]); - params[0] = 0x03; - - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, - &res_size); - - do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, - res_reg, &res_size); - - if ((res_size < 2) - || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_ERR PFX - "Params: %x %x %x %x %x %x %x\n", - params[0], params[1], params[2], - params[3], params[4], params[5], - params[6]); - printk(KERN_ERR PFX - "Error %s (CDROMPLAYTRKIND)\n", - translate_error(res_reg[1])); - retval = -EIO; - break; - } - - /* Save the final position for pauses and resumes */ - final_pos_msf[0] = bcd_to_int(params[4]); - final_pos_msf[1] = bcd_to_int(params[5]); - final_pos_msf[2] = bcd_to_int(params[6]); - sony_audio_status = CDROM_AUDIO_PLAY; - retval = 0; - break; - } - - case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ - { - struct cdrom_volctrl *volctrl = - (struct cdrom_volctrl *) arg; - - params[0] = SONY_SD_AUDIO_VOLUME; - params[1] = volctrl->channel0; - params[2] = volctrl->channel1; - retval = do_sony_cd_cmd_chk("VOLCTRL", - SONY_SET_DRIVE_PARAM_CMD, - params, 3, res_reg, - &res_size); - break; - } - case CDROMSUBCHNL: /* Get subchannel info */ - retval = sony_get_subchnl_info((struct cdrom_subchnl *) arg); - break; - - default: - retval = -EINVAL; - break; - } - up(&sony_sem); - return retval; -} - -static int scd_read_audio(struct cdrom_device_info *cdi, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int retval; - - if (down_interruptible(&sony_sem)) - return -ERESTARTSYS; - switch (cmd) { - case CDROMREADAUDIO: /* Read 2352 byte audio tracks and 2340 byte - raw data tracks. */ - { - struct cdrom_read_audio ra; - - - sony_get_toc(); - if (!sony_toc_read) { - retval = -EIO; - break; - } - - if (copy_from_user(&ra, argp, sizeof(ra))) { - retval = -EFAULT; - break; - } - - if (ra.nframes == 0) { - retval = 0; - break; - } - - if (!access_ok(VERIFY_WRITE, ra.buf, - CD_FRAMESIZE_RAW * ra.nframes)) - return -EFAULT; - - if (ra.addr_format == CDROM_LBA) { - if ((ra.addr.lba >= - sony_toc.lead_out_start_lba) - || (ra.addr.lba + ra.nframes >= - sony_toc.lead_out_start_lba)) { - retval = -EINVAL; - break; - } - } else if (ra.addr_format == CDROM_MSF) { - if ((ra.addr.msf.minute >= 75) - || (ra.addr.msf.second >= 60) - || (ra.addr.msf.frame >= 75)) { - retval = -EINVAL; - break; - } - - ra.addr.lba = ((ra.addr.msf.minute * 4500) - + (ra.addr.msf.second * 75) - + ra.addr.msf.frame); - if ((ra.addr.lba >= - sony_toc.lead_out_start_lba) - || (ra.addr.lba + ra.nframes >= - sony_toc.lead_out_start_lba)) { - retval = -EINVAL; - break; - } - - /* I know, this can go negative on an unsigned. However, - the first thing done to the data is to add this value, - so this should compensate and allow direct msf access. */ - ra.addr.lba -= LOG_START_OFFSET; - } else { - retval = -EINVAL; - break; - } - - retval = read_audio(&ra); - break; - } - retval = 0; - break; - - default: - retval = -EINVAL; - } - up(&sony_sem); - return retval; -} - -static int scd_spinup(void) -{ - unsigned char res_reg[12]; - unsigned int res_size; - int num_spin_ups; - - num_spin_ups = 0; - - respinup_on_open: - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - - /* The drive sometimes returns error 0. I don't know why, but ignore - it. It seems to mean the drive has already done the operation. */ - if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) { - printk(KERN_ERR PFX "%s error (scd_open, spin up)\n", - translate_error(res_reg[1])); - return 1; - } - - do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size); - - /* The drive sometimes returns error 0. I don't know why, but ignore - it. It seems to mean the drive has already done the operation. */ - if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) { - /* If the drive is already playing, it's ok. */ - if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) - || (res_reg[1] == 0)) { - return 0; - } - - /* If the drive says it is not spun up (even though we just did it!) - then retry the operation at least a few times. */ - if ((res_reg[1] == SONY_NOT_SPIN_ERR) - && (num_spin_ups < MAX_CDU31A_RETRIES)) { - num_spin_ups++; - goto respinup_on_open; - } - - printk(KERN_ERR PFX "Error %s (scd_open, read toc)\n", - translate_error(res_reg[1])); - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, - &res_size); - return 1; - } - return 0; -} - -/* - * Open the drive for operations. Spin the drive up and read the table of - * contents if these have not already been done. - */ -static int scd_open(struct cdrom_device_info *cdi, int purpose) -{ - unsigned char res_reg[12]; - unsigned int res_size; - unsigned char params[2]; - - if (purpose == 1) { - /* Open for IOCTLs only - no media check */ - sony_usage++; - return 0; - } - - if (sony_usage == 0) { - if (scd_spinup() != 0) - return -EIO; - sony_get_toc(); - if (!sony_toc_read) { - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, - res_reg, &res_size); - return -EIO; - } - - /* For XA on the CDU31A only, we have to do special reads. - The CDU33A handles XA automagically. */ - /* if ( (sony_toc.disk_type == SONY_XA_DISK_TYPE) */ - if ((sony_toc.disk_type != 0x00) - && (!is_double_speed)) { - params[0] = SONY_SD_DECODE_PARAM; - params[1] = 0x07; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, 2, res_reg, &res_size); - if ((res_size < 2) - || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_WARNING PFX "Unable to set " - "XA params: 0x%2.2x\n", res_reg[1]); - } - sony_xa_mode = 1; - } - /* A non-XA disk. Set the parms back if necessary. */ - else if (sony_xa_mode) { - params[0] = SONY_SD_DECODE_PARAM; - params[1] = 0x0f; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, 2, res_reg, &res_size); - if ((res_size < 2) - || ((res_reg[0] & 0xf0) == 0x20)) { - printk(KERN_WARNING PFX "Unable to reset " - "XA params: 0x%2.2x\n", res_reg[1]); - } - sony_xa_mode = 0; - } - - sony_spun_up = 1; - } - - sony_usage++; - - return 0; -} - - -/* - * Close the drive. Spin it down if no task is using it. The spin - * down will fail if playing audio, so audio play is OK. - */ -static void scd_release(struct cdrom_device_info *cdi) -{ - if (sony_usage == 1) { - unsigned char res_reg[12]; - unsigned int res_size; - - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, - &res_size); - - sony_spun_up = 0; - } - sony_usage--; -} - -static struct cdrom_device_ops scd_dops = { - .open = scd_open, - .release = scd_release, - .drive_status = scd_drive_status, - .media_changed = scd_media_changed, - .tray_move = scd_tray_move, - .lock_door = scd_lock_door, - .select_speed = scd_select_speed, - .get_last_session = scd_get_last_session, - .get_mcn = scd_get_mcn, - .reset = scd_reset, - .audio_ioctl = scd_audio_ioctl, - .capability = CDC_OPEN_TRAY | CDC_CLOSE_TRAY | CDC_LOCK | - CDC_SELECT_SPEED | CDC_MULTI_SESSION | - CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | - CDC_RESET | CDC_DRIVE_STATUS, - .n_minors = 1, -}; - -static struct cdrom_device_info scd_info = { - .ops = &scd_dops, - .speed = 2, - .capacity = 1, - .name = "cdu31a" -}; - -static int scd_block_open(struct inode *inode, struct file *file) -{ - return cdrom_open(&scd_info, inode, file); -} - -static int scd_block_release(struct inode *inode, struct file *file) -{ - return cdrom_release(&scd_info, file); -} - -static int scd_block_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) -{ - int retval; - - /* The eject and close commands should be handled by Uniform CD-ROM - * driver - but I always got hard lockup instead of eject - * until I put this here. - */ - switch (cmd) { - case CDROMEJECT: - scd_lock_door(&scd_info, 0); - retval = scd_tray_move(&scd_info, 1); - break; - case CDROMCLOSETRAY: - retval = scd_tray_move(&scd_info, 0); - break; - case CDROMREADAUDIO: - retval = scd_read_audio(&scd_info, CDROMREADAUDIO, arg); - break; - default: - retval = cdrom_ioctl(file, &scd_info, inode, cmd, arg); - } - return retval; -} - -static int scd_block_media_changed(struct gendisk *disk) -{ - return cdrom_media_changed(&scd_info); -} - -static struct block_device_operations scd_bdops = -{ - .owner = THIS_MODULE, - .open = scd_block_open, - .release = scd_block_release, - .ioctl = scd_block_ioctl, - .media_changed = scd_block_media_changed, -}; - -static struct gendisk *scd_gendisk; - -/* The different types of disc loading mechanisms supported */ -static char *load_mech[] __initdata = - { "caddy", "tray", "pop-up", "unknown" }; - -static int __init -get_drive_configuration(unsigned short base_io, - unsigned char res_reg[], unsigned int *res_size) -{ - unsigned long retry_count; - - - if (!request_region(base_io, 4, "cdu31a")) - return 0; - - /* Set the base address */ - cdu31a_port = base_io; - - /* Set up all the register locations */ - sony_cd_cmd_reg = cdu31a_port + SONY_CMD_REG_OFFSET; - sony_cd_param_reg = cdu31a_port + SONY_PARAM_REG_OFFSET; - sony_cd_write_reg = cdu31a_port + SONY_WRITE_REG_OFFSET; - sony_cd_control_reg = cdu31a_port + SONY_CONTROL_REG_OFFSET; - sony_cd_status_reg = cdu31a_port + SONY_STATUS_REG_OFFSET; - sony_cd_result_reg = cdu31a_port + SONY_RESULT_REG_OFFSET; - sony_cd_read_reg = cdu31a_port + SONY_READ_REG_OFFSET; - sony_cd_fifost_reg = cdu31a_port + SONY_FIFOST_REG_OFFSET; - - /* - * Check to see if anything exists at the status register location. - * I don't know if this is a good way to check, but it seems to work - * ok for me. - */ - if (read_status_register() != 0xff) { - /* - * Reset the drive and wait for attention from it (to say it's reset). - * If you don't wait, the next operation will probably fail. - */ - reset_drive(); - retry_count = jiffies + SONY_RESET_TIMEOUT; - while (time_before(jiffies, retry_count) - && (!is_attention())) { - sony_sleep(); - } - -#if 0 - /* If attention is never seen probably not a CDU31a present */ - if (!is_attention()) { - res_reg[0] = 0x20; - goto out_err; - } -#endif - - /* - * Get the drive configuration. - */ - do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD, - NULL, - 0, (unsigned char *) res_reg, res_size); - if (*res_size <= 2 || (res_reg[0] & 0xf0) != 0) - goto out_err; - return 1; - } - - /* Return an error */ - res_reg[0] = 0x20; -out_err: - release_region(cdu31a_port, 4); - cdu31a_port = 0; - return 0; -} - -#ifndef MODULE -/* - * Set up base I/O and interrupts, called from main.c. - */ - -static int __init cdu31a_setup(char *strings) -{ - int ints[4]; - - (void) get_options(strings, ARRAY_SIZE(ints), ints); - - if (ints[0] > 0) { - cdu31a_port = ints[1]; - } - if (ints[0] > 1) { - cdu31a_irq = ints[2]; - } - if ((strings != NULL) && (*strings != '\0')) { - if (strcmp(strings, "PAS") == 0) { - sony_pas_init = 1; - } else { - printk(KERN_NOTICE PFX "Unknown interface type: %s\n", - strings); - } - } - - return 1; -} - -__setup("cdu31a=", cdu31a_setup); - -#endif - -/* - * Initialize the driver. - */ -int __init cdu31a_init(void) -{ - struct s_sony_drive_config drive_config; - struct gendisk *disk; - int deficiency = 0; - unsigned int res_size; - char msg[255]; - char buf[40]; - int i; - int tmp_irq; - - /* - * According to Alex Freed (freed@europa.orion.adobe.com), this is - * required for the Fusion CD-16 package. If the sound driver is - * loaded, it should work fine, but just in case... - * - * The following turn on the CD-ROM interface for a Fusion CD-16. - */ - if (sony_pas_init) { - outb(0xbc, 0x9a01); - outb(0xe2, 0x9a01); - } - - /* Setting the base I/O address to 0xffff will disable it. */ - if (cdu31a_port == 0xffff) - goto errout3; - - if (cdu31a_port != 0) { - /* Need IRQ 0 because we can't sleep here. */ - tmp_irq = cdu31a_irq; - cdu31a_irq = 0; - if (!get_drive_configuration(cdu31a_port, - drive_config.exec_status, - &res_size)) - goto errout3; - cdu31a_irq = tmp_irq; - } else { - cdu31a_irq = 0; - for (i = 0; cdu31a_addresses[i].base; i++) { - if (get_drive_configuration(cdu31a_addresses[i].base, - drive_config.exec_status, - &res_size)) { - cdu31a_irq = cdu31a_addresses[i].int_num; - break; - } - } - if (!cdu31a_port) - goto errout3; - } - - if (register_blkdev(MAJOR_NR, "cdu31a")) - goto errout2; - - disk = alloc_disk(1); - if (!disk) - goto errout1; - disk->major = MAJOR_NR; - disk->first_minor = 0; - sprintf(disk->disk_name, "cdu31a"); - disk->fops = &scd_bdops; - disk->flags = GENHD_FL_CD; - - if (SONY_HWC_DOUBLE_SPEED(drive_config)) - is_double_speed = 1; - - tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */ - cdu31a_irq = 0; - - sony_speed = is_double_speed; /* Set 2X drives to 2X by default */ - set_drive_params(sony_speed); - - cdu31a_irq = tmp_irq; - - if (cdu31a_irq > 0) { - if (request_irq - (cdu31a_irq, cdu31a_interrupt, IRQF_DISABLED, - "cdu31a", NULL)) { - printk(KERN_WARNING PFX "Unable to grab IRQ%d for " - "the CDU31A driver\n", cdu31a_irq); - cdu31a_irq = 0; - } - } - - sprintf(msg, "Sony I/F CDROM : %8.8s %16.16s %8.8s\n", - drive_config.vendor_id, - drive_config.product_id, - drive_config.product_rev_level); - sprintf(buf, " Capabilities: %s", - load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]); - strcat(msg, buf); - if (SONY_HWC_AUDIO_PLAYBACK(drive_config)) - strcat(msg, ", audio"); - else - deficiency |= CDC_PLAY_AUDIO; - if (SONY_HWC_EJECT(drive_config)) - strcat(msg, ", eject"); - else - deficiency |= CDC_OPEN_TRAY; - if (SONY_HWC_LED_SUPPORT(drive_config)) - strcat(msg, ", LED"); - if (SONY_HWC_ELECTRIC_VOLUME(drive_config)) - strcat(msg, ", elec. Vol"); - if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config)) - strcat(msg, ", sep. Vol"); - if (is_double_speed) - strcat(msg, ", double speed"); - else - deficiency |= CDC_SELECT_SPEED; - if (cdu31a_irq > 0) { - sprintf(buf, ", irq %d", cdu31a_irq); - strcat(msg, buf); - } - strcat(msg, "\n"); - printk(KERN_INFO PFX "%s",msg); - - cdu31a_queue = blk_init_queue(do_cdu31a_request, &cdu31a_lock); - if (!cdu31a_queue) - goto errout0; - blk_queue_hardsect_size(cdu31a_queue, 2048); - - init_timer(&cdu31a_abort_timer); - cdu31a_abort_timer.function = handle_abort_timeout; - - scd_info.mask = deficiency; - scd_gendisk = disk; - if (register_cdrom(&scd_info)) - goto err; - disk->queue = cdu31a_queue; - add_disk(disk); - - disk_changed = 1; - return 0; - -err: - blk_cleanup_queue(cdu31a_queue); -errout0: - if (cdu31a_irq) - free_irq(cdu31a_irq, NULL); - printk(KERN_ERR PFX "Unable to register with Uniform cdrom driver\n"); - put_disk(disk); -errout1: - if (unregister_blkdev(MAJOR_NR, "cdu31a")) { - printk(KERN_WARNING PFX "Can't unregister block device\n"); - } -errout2: - release_region(cdu31a_port, 4); -errout3: - return -EIO; -} - - -static void __exit cdu31a_exit(void) -{ - del_gendisk(scd_gendisk); - put_disk(scd_gendisk); - if (unregister_cdrom(&scd_info)) { - printk(KERN_WARNING PFX "Can't unregister from Uniform " - "cdrom driver\n"); - return; - } - if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL)) { - printk(KERN_WARNING PFX "Can't unregister\n"); - return; - } - - blk_cleanup_queue(cdu31a_queue); - - if (cdu31a_irq > 0) - free_irq(cdu31a_irq, NULL); - - release_region(cdu31a_port, 4); - printk(KERN_INFO PFX "module released.\n"); -} - -#ifdef MODULE -module_init(cdu31a_init); -#endif -module_exit(cdu31a_exit); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_BLOCKDEV_MAJOR(CDU31A_CDROM_MAJOR); diff --git a/drivers/cdrom/cdu31a.h b/drivers/cdrom/cdu31a.h deleted file mode 100644 index 61d4768c412..00000000000 --- a/drivers/cdrom/cdu31a.h +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Definitions for a Sony interface CDROM drive. - * - * Corey Minyard (minyard@wf-rch.cirr.com) - * - * Copyright (C) 1993 Corey Minyard - * - * 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. - * - */ - -/* - * General defines. - */ -#define SONY_XA_DISK_TYPE 0x20 - -/* - * Offsets (from the base address) and bits for the various write registers - * of the drive. - */ -#define SONY_CMD_REG_OFFSET 0 -#define SONY_PARAM_REG_OFFSET 1 -#define SONY_WRITE_REG_OFFSET 2 -#define SONY_CONTROL_REG_OFFSET 3 -# define SONY_ATTN_CLR_BIT 0x01 -# define SONY_RES_RDY_CLR_BIT 0x02 -# define SONY_DATA_RDY_CLR_BIT 0x04 -# define SONY_ATTN_INT_EN_BIT 0x08 -# define SONY_RES_RDY_INT_EN_BIT 0x10 -# define SONY_DATA_RDY_INT_EN_BIT 0x20 -# define SONY_PARAM_CLR_BIT 0x40 -# define SONY_DRIVE_RESET_BIT 0x80 - -/* - * Offsets (from the base address) and bits for the various read registers - * of the drive. - */ -#define SONY_STATUS_REG_OFFSET 0 -# define SONY_ATTN_BIT 0x01 -# define SONY_RES_RDY_BIT 0x02 -# define SONY_DATA_RDY_BIT 0x04 -# define SONY_ATTN_INT_ST_BIT 0x08 -# define SONY_RES_RDY_INT_ST_BIT 0x10 -# define SONY_DATA_RDY_INT_ST_BIT 0x20 -# define SONY_DATA_REQUEST_BIT 0x40 -# define SONY_BUSY_BIT 0x80 -#define SONY_RESULT_REG_OFFSET 1 -#define SONY_READ_REG_OFFSET 2 -#define SONY_FIFOST_REG_OFFSET 3 -# define SONY_PARAM_WRITE_RDY_BIT 0x01 -# define SONY_PARAM_REG_EMPTY_BIT 0x02 -# define SONY_RES_REG_NOT_EMP_BIT 0x04 -# define SONY_RES_REG_FULL_BIT 0x08 - -#define LOG_START_OFFSET 150 /* Offset of first logical sector */ - -#define SONY_DETECT_TIMEOUT (8*HZ/10) /* Maximum amount of time - that drive detection code - will wait for response - from drive (in 1/100th's - of seconds). */ - -#define SONY_JIFFIES_TIMEOUT (10*HZ) /* Maximum number of times the - drive will wait/try for an - operation */ -#define SONY_RESET_TIMEOUT HZ /* Maximum number of times the - drive will wait/try a reset - operation */ -#define SONY_READY_RETRIES 20000 /* How many times to retry a - spin waiting for a register - to come ready */ - -#define MAX_CDU31A_RETRIES 3 /* How many times to retry an - operation */ - -/* Commands to request or set drive control parameters and disc information */ -#define SONY_REQ_DRIVE_CONFIG_CMD 0x00 /* Returns s_sony_drive_config */ -#define SONY_REQ_DRIVE_MODE_CMD 0x01 -#define SONY_REQ_DRIVE_PARAM_CMD 0x02 -#define SONY_REQ_MECH_STATUS_CMD 0x03 -#define SONY_REQ_AUDIO_STATUS_CMD 0x04 -#define SONY_SET_DRIVE_PARAM_CMD 0x10 -#define SONY_REQ_TOC_DATA_CMD 0x20 /* Returns s_sony_toc */ -#define SONY_REQ_SUBCODE_ADDRESS_CMD 0x21 /* Returns s_sony_subcode */ -#define SONY_REQ_UPC_EAN_CMD 0x22 -#define SONY_REQ_ISRC_CMD 0x23 -#define SONY_REQ_TOC_DATA_SPEC_CMD 0x24 /* Returns s_sony_session_toc */ - -/* Commands to request information from the drive */ -#define SONY_READ_TOC_CMD 0x30 /* let the drive firmware grab the TOC */ -#define SONY_SEEK_CMD 0x31 -#define SONY_READ_CMD 0x32 -#define SONY_READ_BLKERR_STAT_CMD 0x34 -#define SONY_ABORT_CMD 0x35 -#define SONY_READ_TOC_SPEC_CMD 0x36 - -/* Commands to control audio */ -#define SONY_AUDIO_PLAYBACK_CMD 0x40 -#define SONY_AUDIO_STOP_CMD 0x41 -#define SONY_AUDIO_SCAN_CMD 0x42 - -/* Miscellaneous control commands */ -#define SONY_EJECT_CMD 0x50 -#define SONY_SPIN_UP_CMD 0x51 -#define SONY_SPIN_DOWN_CMD 0x52 - -/* Diagnostic commands */ -#define SONY_WRITE_BUFFER_CMD 0x60 -#define SONY_READ_BUFFER_CMD 0x61 -#define SONY_DIAGNOSTICS_CMD 0x62 - - -/* - * The following are command parameters for the set drive parameter command - */ -#define SONY_SD_DECODE_PARAM 0x00 -#define SONY_SD_INTERFACE_PARAM 0x01 -#define SONY_SD_BUFFERING_PARAM 0x02 -#define SONY_SD_AUDIO_PARAM 0x03 -#define SONY_SD_AUDIO_VOLUME 0x04 -#define SONY_SD_MECH_CONTROL 0x05 -#define SONY_SD_AUTO_SPIN_DOWN_TIME 0x06 - -/* - * The following are parameter bits for the mechanical control command - */ -#define SONY_AUTO_SPIN_UP_BIT 0x01 -#define SONY_AUTO_EJECT_BIT 0x02 -#define SONY_DOUBLE_SPEED_BIT 0x04 - -/* - * The following extract information from the drive configuration about - * the drive itself. - */ -#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03) -#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04) -#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08) -#define SONY_HWC_DOUBLE_SPEED(c) (c.hw_config[0] & 0x10) -#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6) -#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01) -#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02) -#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04) - -#define SONY_HWC_CADDY_LOAD_MECH 0x00 -#define SONY_HWC_TRAY_LOAD_MECH 0x01 -#define SONY_HWC_POPUP_LOAD_MECH 0x02 -#define SONY_HWC_UNKWN_LOAD_MECH 0x03 - -#define SONY_HWC_8KB_BUFFER 0x00 -#define SONY_HWC_32KB_BUFFER 0x01 -#define SONY_HWC_64KB_BUFFER 0x02 -#define SONY_HWC_UNKWN_BUFFER 0x03 - -/* - * This is the complete status returned from the drive configuration request - * command. - */ -struct s_sony_drive_config -{ - unsigned char exec_status[2]; - char vendor_id[8]; - char product_id[16]; - char product_rev_level[8]; - unsigned char hw_config[2]; -}; - -/* The following is returned from the request subcode address command */ -struct s_sony_subcode -{ - unsigned char exec_status[2]; - unsigned char address :4; - unsigned char control :4; - unsigned char track_num; - unsigned char index_num; - unsigned char rel_msf[3]; - unsigned char reserved1; - unsigned char abs_msf[3]; -}; - -#define MAX_TRACKS 100 /* The maximum tracks a disk may have. */ -/* - * The following is returned from the request TOC (Table Of Contents) command. - * (last_track_num-first_track_num+1) values are valid in tracks. - */ -struct s_sony_toc -{ - unsigned char exec_status[2]; - unsigned char address0 :4; - unsigned char control0 :4; - unsigned char point0; - unsigned char first_track_num; - unsigned char disk_type; - unsigned char dummy0; - unsigned char address1 :4; - unsigned char control1 :4; - unsigned char point1; - unsigned char last_track_num; - unsigned char dummy1; - unsigned char dummy2; - unsigned char address2 :4; - unsigned char control2 :4; - unsigned char point2; - unsigned char lead_out_start_msf[3]; - struct - { - unsigned char address :4; - unsigned char control :4; - unsigned char track; - unsigned char track_start_msf[3]; - } tracks[MAX_TRACKS]; - - unsigned int lead_out_start_lba; -}; - -struct s_sony_session_toc -{ - unsigned char exec_status[2]; - unsigned char session_number; - unsigned char address0 :4; - unsigned char control0 :4; - unsigned char point0; - unsigned char first_track_num; - unsigned char disk_type; - unsigned char dummy0; - unsigned char address1 :4; - unsigned char control1 :4; - unsigned char point1; - unsigned char last_track_num; - unsigned char dummy1; - unsigned char dummy2; - unsigned char address2 :4; - unsigned char control2 :4; - unsigned char point2; - unsigned char lead_out_start_msf[3]; - unsigned char addressb0 :4; - unsigned char controlb0 :4; - unsigned char pointb0; - unsigned char next_poss_prog_area_msf[3]; - unsigned char num_mode_5_pointers; - unsigned char max_start_outer_leadout_msf[3]; - unsigned char addressb1 :4; - unsigned char controlb1 :4; - unsigned char pointb1; - unsigned char dummyb0_1[4]; - unsigned char num_skip_interval_pointers; - unsigned char num_skip_track_assignments; - unsigned char dummyb0_2; - unsigned char addressb2 :4; - unsigned char controlb2 :4; - unsigned char pointb2; - unsigned char tracksb2[7]; - unsigned char addressb3 :4; - unsigned char controlb3 :4; - unsigned char pointb3; - unsigned char tracksb3[7]; - unsigned char addressb4 :4; - unsigned char controlb4 :4; - unsigned char pointb4; - unsigned char tracksb4[7]; - unsigned char addressc0 :4; - unsigned char controlc0 :4; - unsigned char pointc0; - unsigned char dummyc0[7]; - struct - { - unsigned char address :4; - unsigned char control :4; - unsigned char track; - unsigned char track_start_msf[3]; - } tracks[MAX_TRACKS]; - - unsigned int start_track_lba; - unsigned int lead_out_start_lba; - unsigned int mint; - unsigned int maxt; -}; - -struct s_all_sessions_toc -{ - unsigned char sessions; - unsigned int track_entries; - unsigned char first_track_num; - unsigned char last_track_num; - unsigned char disk_type; - unsigned char lead_out_start_msf[3]; - struct - { - unsigned char address :4; - unsigned char control :4; - unsigned char track; - unsigned char track_start_msf[3]; - } tracks[MAX_TRACKS]; - - unsigned int start_track_lba; - unsigned int lead_out_start_lba; -}; - - -/* - * The following are errors returned from the drive. - */ - -/* Command error group */ -#define SONY_ILL_CMD_ERR 0x10 -#define SONY_ILL_PARAM_ERR 0x11 - -/* Mechanism group */ -#define SONY_NOT_LOAD_ERR 0x20 -#define SONY_NO_DISK_ERR 0x21 -#define SONY_NOT_SPIN_ERR 0x22 -#define SONY_SPIN_ERR 0x23 -#define SONY_SPINDLE_SERVO_ERR 0x25 -#define SONY_FOCUS_SERVO_ERR 0x26 -#define SONY_EJECT_MECH_ERR 0x29 -#define SONY_AUDIO_PLAYING_ERR 0x2a -#define SONY_EMERGENCY_EJECT_ERR 0x2c - -/* Seek error group */ -#define SONY_FOCUS_ERR 0x30 -#define SONY_FRAME_SYNC_ERR 0x31 -#define SONY_SUBCODE_ADDR_ERR 0x32 -#define SONY_BLOCK_SYNC_ERR 0x33 -#define SONY_HEADER_ADDR_ERR 0x34 - -/* Read error group */ -#define SONY_ILL_TRACK_R_ERR 0x40 -#define SONY_MODE_0_R_ERR 0x41 -#define SONY_ILL_MODE_R_ERR 0x42 -#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43 -#define SONY_MODE_R_ERR 0x44 -#define SONY_FORM_R_ERR 0x45 -#define SONY_LEAD_OUT_R_ERR 0x46 -#define SONY_BUFFER_OVERRUN_R_ERR 0x47 - -/* Data error group */ -#define SONY_UNREC_CIRC_ERR 0x53 -#define SONY_UNREC_LECC_ERR 0x57 - -/* Subcode error group */ -#define SONY_NO_TOC_ERR 0x60 -#define SONY_SUBCODE_DATA_NVAL_ERR 0x61 -#define SONY_FOCUS_ON_TOC_READ_ERR 0x63 -#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64 -#define SONY_TOC_DATA_ERR 0x65 - -/* Hardware failure group */ -#define SONY_HW_FAILURE_ERR 0x70 -#define SONY_LEAD_IN_A_ERR 0x91 -#define SONY_LEAD_OUT_A_ERR 0x92 -#define SONY_DATA_TRACK_A_ERR 0x93 - -/* - * The following are returned from the Read With Block Error Status command. - * They are not errors but information (Errors from the 0x5x group above may - * also be returned - */ -#define SONY_NO_CIRC_ERR_BLK_STAT 0x50 -#define SONY_NO_LECC_ERR_BLK_STAT 0x54 -#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55 -#define SONY_NO_ERR_DETECTION_STAT 0x59 - -/* - * The following is not an error returned by the drive, but by the code - * that talks to the drive. It is returned because of a timeout. - */ -#define SONY_TIMEOUT_OP_ERR 0x01 -#define SONY_SIGNAL_OP_ERR 0x02 -#define SONY_BAD_DATA_ERR 0x03 - - -/* - * The following are attention code for asynchronous events from the drive. - */ - -/* Standard attention group */ -#define SONY_EMER_EJECT_ATTN 0x2c -#define SONY_HW_FAILURE_ATTN 0x70 -#define SONY_MECH_LOADED_ATTN 0x80 -#define SONY_EJECT_PUSHED_ATTN 0x81 - -/* Audio attention group */ -#define SONY_AUDIO_PLAY_DONE_ATTN 0x90 -#define SONY_LEAD_IN_ERR_ATTN 0x91 -#define SONY_LEAD_OUT_ERR_ATTN 0x92 -#define SONY_DATA_TRACK_ERR_ATTN 0x93 -#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94 - -/* Auto spin up group */ -#define SONY_SPIN_UP_COMPLETE_ATTN 0x24 -#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25 -#define SONY_FOCUS_SERVO_ERR_ATTN 0x26 -#define SONY_TOC_READ_DONE_ATTN 0x62 -#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63 -#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65 - -/* Auto eject group */ -#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27 -#define SONY_EJECT_COMPLETE_ATTN 0x28 -#define SONY_EJECT_MECH_ERR_ATTN 0x29 diff --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c deleted file mode 100644 index 23013116324..00000000000 --- a/drivers/cdrom/cm206.c +++ /dev/null @@ -1,1594 +0,0 @@ -/* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card. - Copyright (c) 1995--1997 David A. van Leeuwen. - $Id: cm206.c,v 1.5 1997/12/26 11:02:51 david Exp $ - - 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. - -History: - Started 25 jan 1994. Waiting for documentation... - 22 feb 1995: 0.1a first reasonably safe polling driver. - Two major bugs, one in read_sector and one in - do_cm206_request, happened to cancel! - 25 feb 1995: 0.2a first reasonable interrupt driven version of above. - uart writes are still done in polling mode. - 25 feb 1995: 0.21a writes also in interrupt mode, still some - small bugs to be found... Larger buffer. - 2 mrt 1995: 0.22 Bug found (cd-> nowhere, interrupt was called in - initialization), read_ahead of 16. Timeouts implemented. - unclear if they do something... - 7 mrt 1995: 0.23 Start of background read-ahead. - 18 mrt 1995: 0.24 Working background read-ahead. (still problems) - 26 mrt 1995: 0.25 Multi-session ioctl added (kernel v1.2). - Statistics implemented, though separate stats206.h. - Accessible through ioctl 0x1000 (just a number). - Hard to choose between v1.2 development and 1.1.75. - Bottom-half doesn't work with 1.2... - 0.25a: fixed... typo. Still problems... - 1 apr 1995: 0.26 Module support added. Most bugs found. Use kernel 1.2.n. - 5 apr 1995: 0.27 Auto-probe for the adapter card base address. - Auto-probe for the adaptor card irq line. - 7 apr 1995: 0.28 Added lilo setup support for base address and irq. - Use major number 32 (not in this source), officially - assigned to this driver. - 9 apr 1995: 0.29 Added very limited audio support. Toc_header, stop, pause, - resume, eject. Play_track ignores track info, because we can't - read a table-of-contents entry. Toc_entry is implemented - as a `placebo' function: always returns start of disc. - 3 may 1995: 0.30 Audio support completed. The get_toc_entry function - is implemented as a binary search. - 15 may 1995: 0.31 More work on audio stuff. Workman is not easy to - satisfy; changed binary search into linear search. - Auto-probe for base address somewhat relaxed. - 1 jun 1995: 0.32 Removed probe_irq_on/off for module version. - 10 jun 1995: 0.33 Workman still behaves funny, but you should be - able to eject and substitute another disc. - - An adaptation of 0.33 is included in linux-1.3.7 by Eberhard Moenkeberg - - 18 jul 1995: 0.34 Patch by Heiko Eissfeldt included, mainly considering - verify_area's in the ioctls. Some bugs introduced by - EM considering the base port and irq fixed. - - 18 dec 1995: 0.35 Add some code for error checking... no luck... - - We jump to reach our goal: version 1.0 in the next stable linux kernel. - - 19 mar 1996: 0.95 Different implementation of CDROM_GET_UPC, on - request of Thomas Quinot. - 25 mar 1996: 0.96 Interpretation of opening with O_WRONLY or O_RDWR: - open only for ioctl operation, e.g., for operation of - tray etc. - 4 apr 1996: 0.97 First implementation of layer between VFS and cdrom - driver, a generic interface. Much of the functionality - of cm206_open() and cm206_ioctl() is transferred to a - new file cdrom.c and its header ucdrom.h. - - Upgrade to Linux kernel 1.3.78. - - 11 apr 1996 0.98 Upgrade to Linux kernel 1.3.85 - More code moved to cdrom.c - - 0.99 Some more small changes to decrease number - of oopses at module load; - - 27 jul 1996 0.100 Many hours of debugging, kernel change from 1.2.13 - to 2.0.7 seems to have introduced some weird behavior - in (interruptible_)sleep_on(&cd->data): the process - seems to be woken without any explicit wake_up in my own - code. Patch to try 100x in case such untriggered wake_up's - occur. - - 28 jul 1996 0.101 Rewriting of the code that receives the command echo, - using a fifo to store echoed bytes. - - Branch from 0.99: - - 0.99.1.0 Update to kernel release 2.0.10 dev_t -> kdev_t - (emoenke) various typos found by others. extra - module-load oops protection. - - 0.99.1.1 Initialization constant cdrom_dops.speed - changed from float (2.0) to int (2); Cli()-sti() pair - around cm260_reset() in module initialization code. - - 0.99.1.2 Changes literally as proposed by Scott Snyder - <snyder@d0sgif.fnal.gov> for the 2.1 kernel line, which - have to do mainly with the poor minor support i had. The - major new concept is to change a cdrom driver's - operations struct from the capabilities struct. This - reflects the fact that there is one major for a driver, - whilst there can be many minors whith completely - different capabilities. - - 0.99.1.3 More changes for operations/info separation. - - 0.99.1.4 Added speed selection (someone had to do this - first). - - 23 jan 1997 0.99.1.5 MODULE_PARMS call added. - - 23 jan 1997 0.100.1.2--0.100.1.5 following similar lines as - 0.99.1.1--0.99.1.5. I get too many complaints about the - drive making read errors. What't wrong with the 2.0+ - kernel line? Why get i (and othe cm206 owners) weird - results? Why were things good in the good old 1.1--1.2 - era? Why don't i throw away the drive? - - 2 feb 1997 0.102 Added `volatile' to values in cm206_struct. Seems to - reduce many of the problems. Rewrote polling routines - to use fixed delays between polls. - 0.103 Changed printk behavior. - 0.104 Added a 0.100 -> 0.100.1.1 change - -11 feb 1997 0.105 Allow auto_probe during module load, disable - with module option "auto_probe=0". Moved some debugging - statements to lower priority. Implemented select_speed() - function. - -13 feb 1997 1.0 Final version for 2.0 kernel line. - - All following changes will be for the 2.1 kernel line. - -15 feb 1997 1.1 Keep up with kernel 2.1.26, merge in changes from - cdrom.c 0.100.1.1--1.0. Add some more MODULE_PARMS. - -14 sep 1997 1.2 Upgrade to Linux 2.1.55. Added blksize_size[], patch - sent by James Bottomley <James.Bottomley@columbiasc.ncr.com>. - -21 dec 1997 1.4 Upgrade to Linux 2.1.72. - -24 jan 1998 Removed the cm206_disc_status() function, as it was now dead - code. The Uniform CDROM driver now provides this functionality. - -9 Nov. 1999 Make kernel-parameter implementation work with 2.3.x - Removed init_module & cleanup_module in favor of - module_init & module_exit. - Torben Mathiasen <tmm@image.dk> - * - * Parts of the code are based upon lmscd.c written by Kai Petzke, - * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin - * Harriss, but any off-the-shelf dynamic programming algorithm won't - * be able to find them. - * - * The cm206 drive interface and the cm260 adapter card seem to be - * sufficiently different from their cm205/cm250 counterparts - * in order to write a complete new driver. - * - * I call all routines connected to the Linux kernel something - * with `cm206' in it, as this stuff is too series-dependent. - * - * Currently, my limited knowledge is based on: - * - The Linux Kernel Hacker's guide, v. 0.5, by Michael K. Johnson - * - Linux Kernel Programmierung, by Michael Beck and others - * - Philips/LMS cm206 and cm226 product specification - * - Philips/LMS cm260 product specification - * - * David van Leeuwen, david@tm.tno.nl. */ -#define REVISION "$Revision: 1.5 $" - -#include <linux/module.h> - -#include <linux/errno.h> /* These include what we really need */ -#include <linux/delay.h> -#include <linux/string.h> -#include <linux/interrupt.h> -#include <linux/timer.h> -#include <linux/cdrom.h> -#include <linux/ioport.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/init.h> - -/* #include <linux/ucdrom.h> */ - -#include <asm/io.h> - -#define MAJOR_NR CM206_CDROM_MAJOR - -#include <linux/blkdev.h> - -#undef DEBUG -#define STATISTICS /* record times and frequencies of events */ -#define AUTO_PROBE_MODULE -#define USE_INSW - -#include "cm206.h" - -/* This variable defines whether or not to probe for adapter base port - address and interrupt request. It can be overridden by the boot - parameter `auto'. -*/ -static int auto_probe = 1; /* Yes, why not? */ - -static int cm206_base = CM206_BASE; -static int cm206_irq = CM206_IRQ; -#ifdef MODULE -static int cm206[2] = { 0, 0 }; /* for compatible `insmod' parameter passing */ -module_param_array(cm206, int, NULL, 0); /* base,irq or irq,base */ -#endif - -module_param(cm206_base, int, 0); /* base */ -module_param(cm206_irq, int, 0); /* irq */ -module_param(auto_probe, bool, 0); /* auto probe base and irq */ -MODULE_LICENSE("GPL"); - -#define POLLOOP 100 /* milliseconds */ -#define READ_AHEAD 1 /* defines private buffer, waste! */ -#define BACK_AHEAD 1 /* defines adapter-read ahead */ -#define DATA_TIMEOUT (3*HZ) /* measured in jiffies (10 ms) */ -#define UART_TIMEOUT (5*HZ/100) -#define DSB_TIMEOUT (7*HZ) /* time for the slowest command to finish */ -#define UR_SIZE 4 /* uart receive buffer fifo size */ - -#define LINUX_BLOCK_SIZE 512 /* WHERE is this defined? */ -#define RAW_SECTOR_SIZE 2352 /* ok, is also defined in cdrom.h */ -#define ISO_SECTOR_SIZE 2048 -#define BLOCKS_ISO (ISO_SECTOR_SIZE/LINUX_BLOCK_SIZE) /* 4 */ -#define CD_SYNC_HEAD 16 /* CD_SYNC + CD_HEAD */ - -#ifdef STATISTICS /* keep track of errors in counters */ -#define stats(i) { ++cd->stats[st_ ## i]; \ - cd->last_stat[st_ ## i] = cd->stat_counter++; \ - } -#else -#define stats(i) (void) 0; -#endif - -#define Debug(a) {printk (KERN_DEBUG); printk a;} -#ifdef DEBUG -#define debug(a) Debug(a) -#else -#define debug(a) (void) 0; -#endif - -typedef unsigned char uch; /* 8-bits */ -typedef unsigned short ush; /* 16-bits */ - -struct toc_struct { /* private copy of Table of Contents */ - uch track, fsm[3], q0; -}; - -struct cm206_struct { - volatile ush intr_ds; /* data status read on last interrupt */ - volatile ush intr_ls; /* uart line status read on last interrupt */ - volatile uch ur[UR_SIZE]; /* uart receive buffer fifo */ - volatile uch ur_w, ur_r; /* write/read buffer index */ - volatile uch dsb, cc; /* drive status byte and condition (error) code */ - int command; /* command to be written to the uart */ - int openfiles; - ush sector[READ_AHEAD * RAW_SECTOR_SIZE / 2]; /* buffered cd-sector */ - int sector_first, sector_last; /* range of these sectors */ - wait_queue_head_t uart; /* wait queues for interrupt */ - wait_queue_head_t data; - struct timer_list timer; /* time-out */ - char timed_out; - signed char max_sectors; /* number of sectors that fit in adapter mem */ - char wait_back; /* we're waiting for a background-read */ - char background; /* is a read going on in the background? */ - int adapter_first; /* if so, that's the starting sector */ - int adapter_last; - char fifo_overflowed; - uch disc_status[7]; /* result of get_disc_status command */ -#ifdef STATISTICS - int stats[NR_STATS]; - int last_stat[NR_STATS]; /* `time' at which stat was stat */ - int stat_counter; -#endif - struct toc_struct toc[101]; /* The whole table of contents + lead-out */ - uch q[10]; /* Last read q-channel info */ - uch audio_status[5]; /* last read position on pause */ - uch media_changed; /* record if media changed */ -}; - -#define DISC_STATUS cd->disc_status[0] -#define FIRST_TRACK cd->disc_status[1] -#define LAST_TRACK cd->disc_status[2] -#define PAUSED cd->audio_status[0] /* misuse this memory byte! */ -#define PLAY_TO cd->toc[0] /* toc[0] records end-time in play */ - -static struct cm206_struct *cd; /* the main memory structure */ -static struct request_queue *cm206_queue; -static DEFINE_SPINLOCK(cm206_lock); - -/* First, we define some polling functions. These are actually - only being used in the initialization. */ - -static void send_command_polled(int command) -{ - int loop = POLLOOP; - while (!(inw(r_line_status) & ls_transmitter_buffer_empty) - && loop > 0) { - mdelay(1); /* one millisec delay */ - --loop; - } - outw(command, r_uart_transmit); -} - -static uch receive_echo_polled(void) -{ - int loop = POLLOOP; - while (!(inw(r_line_status) & ls_receive_buffer_full) && loop > 0) { - mdelay(1); - --loop; - } - return ((uch) inw(r_uart_receive)); -} - -static uch send_receive_polled(int command) -{ - send_command_polled(command); - return receive_echo_polled(); -} - -static inline void clear_ur(void) -{ - if (cd->ur_r != cd->ur_w) { - debug(("Deleting bytes from fifo:")); - for (; cd->ur_r != cd->ur_w; - cd->ur_r++, cd->ur_r %= UR_SIZE) - debug((" 0x%x", cd->ur[cd->ur_r])); - debug(("\n")); - } -} - -static struct tasklet_struct cm206_tasklet; - -/* The interrupt handler. When the cm260 generates an interrupt, very - much care has to be taken in reading out the registers in the right - order; in case of a receive_buffer_full interrupt, first the - uart_receive must be read, and then the line status again to - de-assert the interrupt line. It took me a couple of hours to find - this out:-( - - The function reset_cm206 appears to cause an interrupt, because - pulling up the INIT line clears both the uart-write-buffer /and/ - the uart-write-buffer-empty mask. We call this a `lost interrupt,' - as there seems so reason for this to happen. -*/ - -static irqreturn_t cm206_interrupt(int sig, void *dev_id) -{ - volatile ush fool; - cd->intr_ds = inw(r_data_status); /* resets data_ready, data_error, - crc_error, sync_error, toc_ready - interrupts */ - cd->intr_ls = inw(r_line_status); /* resets overrun bit */ - debug(("Intr, 0x%x 0x%x, %d\n", cd->intr_ds, cd->intr_ls, - cd->background)); - if (cd->intr_ls & ls_attention) - stats(attention); - /* receive buffer full? */ - if (cd->intr_ls & ls_receive_buffer_full) { - cd->ur[cd->ur_w] = inb(r_uart_receive); /* get order right! */ - cd->intr_ls = inw(r_line_status); /* resets rbf interrupt */ - debug(("receiving #%d: 0x%x\n", cd->ur_w, - cd->ur[cd->ur_w])); - cd->ur_w++; - cd->ur_w %= UR_SIZE; - if (cd->ur_w == cd->ur_r) - debug(("cd->ur overflow!\n")); - if (waitqueue_active(&cd->uart) && cd->background < 2) { - del_timer(&cd->timer); - wake_up_interruptible(&cd->uart); - } - } - /* data ready in fifo? */ - else if (cd->intr_ds & ds_data_ready) { - if (cd->background) - ++cd->adapter_last; - if (waitqueue_active(&cd->data) - && (cd->wait_back || !cd->background)) { - del_timer(&cd->timer); - wake_up_interruptible(&cd->data); - } - stats(data_ready); - } - /* ready to issue a write command? */ - else if (cd->command && cd->intr_ls & ls_transmitter_buffer_empty) { - outw(dc_normal | (inw(r_data_status) & 0x7f), - r_data_control); - outw(cd->command, r_uart_transmit); - cd->command = 0; - if (!cd->background) - wake_up_interruptible(&cd->uart); - } - /* now treat errors (at least, identify them for debugging) */ - else if (cd->intr_ds & ds_fifo_overflow) { - debug(("Fifo overflow at sectors 0x%x\n", - cd->sector_first)); - fool = inw(r_fifo_output_buffer); /* de-assert the interrupt */ - cd->fifo_overflowed = 1; /* signal one word less should be read */ - stats(fifo_overflow); - } else if (cd->intr_ds & ds_data_error) { - debug(("Data error at sector 0x%x\n", cd->sector_first)); - stats(data_error); - } else if (cd->intr_ds & ds_crc_error) { - debug(("CRC error at sector 0x%x\n", cd->sector_first)); - stats(crc_error); - } else if (cd->intr_ds & ds_sync_error) { - debug(("Sync at sector 0x%x\n", cd->sector_first)); - stats(sync_error); - } else if (cd->intr_ds & ds_toc_ready) { - /* do something appropriate */ - } - /* couldn't see why this interrupt, maybe due to init */ - else { - outw(dc_normal | READ_AHEAD, r_data_control); - stats(lost_intr); - } - if (cd->background - && (cd->adapter_last - cd->adapter_first == cd->max_sectors - || cd->fifo_overflowed)) - tasklet_schedule(&cm206_tasklet); /* issue a stop read command */ - stats(interrupt); - return IRQ_HANDLED; -} - -/* we have put the address of the wait queue in who */ -static void cm206_timeout(unsigned long who) -{ - cd->timed_out = 1; - debug(("Timing out\n")); - wake_up_interruptible((wait_queue_head_t *) who); -} - -/* This function returns 1 if a timeout occurred, 0 if an interrupt - happened */ -static int sleep_or_timeout(wait_queue_head_t * wait, int timeout) -{ - cd->timed_out = 0; - init_timer(&cd->timer); - cd->timer.data = (unsigned long) wait; - cd->timer.expires = jiffies + timeout; - add_timer(&cd->timer); - debug(("going to sleep\n")); - interruptible_sleep_on(wait); - del_timer(&cd->timer); - if (cd->timed_out) { - cd->timed_out = 0; - return 1; - } else - return 0; -} - -static void send_command(int command) -{ - debug(("Sending 0x%x\n", command)); - if (!(inw(r_line_status) & ls_transmitter_buffer_empty)) { - cd->command = command; - cli(); /* don't interrupt before sleep */ - outw(dc_mask_sync_error | dc_no_stop_on_error | - (inw(r_data_status) & 0x7f), r_data_control); - /* interrupt routine sends command */ - if (sleep_or_timeout(&cd->uart, UART_TIMEOUT)) { - debug(("Time out on write-buffer\n")); - stats(write_timeout); - outw(command, r_uart_transmit); - } - debug(("Write commmand delayed\n")); - } else - outw(command, r_uart_transmit); -} - -static uch receive_byte(int timeout) -{ - uch ret; - cli(); - debug(("cli\n")); - ret = cd->ur[cd->ur_r]; - if (cd->ur_r != cd->ur_w) { - sti(); - debug(("returning #%d: 0x%x\n", cd->ur_r, - cd->ur[cd->ur_r])); - cd->ur_r++; - cd->ur_r %= UR_SIZE; - return ret; - } else if (sleep_or_timeout(&cd->uart, timeout)) { /* does sti() */ - debug(("Time out on receive-buffer\n")); -#ifdef STATISTICS - if (timeout == UART_TIMEOUT) - stats(receive_timeout) /* no `;'! */ - else - stats(dsb_timeout); -#endif - return 0xda; - } - ret = cd->ur[cd->ur_r]; - debug(("slept; returning #%d: 0x%x\n", cd->ur_r, - cd->ur[cd->ur_r])); - cd->ur_r++; - cd->ur_r %= UR_SIZE; - return ret; -} - -static inline uch receive_echo(void) -{ - return receive_byte(UART_TIMEOUT); -} - -static inline uch send_receive(int command) -{ - send_command(command); - return receive_echo(); -} - -static inline uch wait_dsb(void) -{ - return receive_byte(DSB_TIMEOUT); -} - -static int type_0_command(int command, int expect_dsb) -{ - int e; - clear_ur(); - if (command != (e = send_receive(command))) { - debug(("command 0x%x echoed as 0x%x\n", command, e)); - stats(echo); - return -1; - } - if (expect_dsb) { - cd->dsb = wait_dsb(); /* wait for command to finish */ - } - return 0; -} - -static int type_1_command(int command, int bytes, uch * status) -{ /* returns info */ - int i; - if (type_0_command(command, 0)) - return -1; - for (i = 0; i < bytes; i++) - status[i] = send_receive(c_gimme); - return 0; -} - -/* This function resets the adapter card. We'd better not do this too - * often, because it tends to generate `lost interrupts.' */ -static void reset_cm260(void) -{ - outw(dc_normal | dc_initialize | READ_AHEAD, r_data_control); - udelay(10); /* 3.3 mu sec minimum */ - outw(dc_normal | READ_AHEAD, r_data_control); -} - -/* fsm: frame-sec-min from linear address; one of many */ -static void fsm(int lba, uch * fsm) -{ - fsm[0] = lba % 75; - lba /= 75; - lba += 2; - fsm[1] = lba % 60; - fsm[2] = lba / 60; -} - -static inline int fsm2lba(uch * fsm) -{ - return fsm[0] + 75 * (fsm[1] - 2 + 60 * fsm[2]); -} - -static inline int f_s_m2lba(uch f, uch s, uch m) -{ - return f + 75 * (s - 2 + 60 * m); -} - -static int start_read(int start) -{ - uch read_sector[4] = { c_read_data, }; - int i, e; - - fsm(start, &read_sector[1]); - clear_ur(); - for (i = 0; i < 4; i++) - if (read_sector[i] != (e = send_receive(read_sector[i]))) { - debug(("read_sector: %x echoes %x\n", - read_sector[i], e)); - stats(echo); - if (e == 0xff) { /* this seems to happen often */ - e = receive_echo(); - debug(("Second try %x\n", e)); - if (e != read_sector[i]) - return -1; - } - } - return 0; -} - -static int stop_read(void) -{ - int e; - type_0_command(c_stop, 0); - if ((e = receive_echo()) != 0xff) { - debug(("c_stop didn't send 0xff, but 0x%x\n", e)); - stats(stop_0xff); - return -1; - } - return 0; -} - -/* This function starts to read sectors in adapter memory, the - interrupt routine should stop the read. In fact, the bottom_half - routine takes care of this. Set a flag `background' in the cd - struct to indicate the process. */ - -static int read_background(int start, int reading) -{ - if (cd->background) - return -1; /* can't do twice */ - outw(dc_normal | BACK_AHEAD, r_data_control); - if (!reading && start_read(start)) - return -2; - cd->adapter_first = cd->adapter_last = start; - cd->background = 1; /* flag a read is going on */ - return 0; -} - -#ifdef USE_INSW -#define transport_data insw -#else -/* this routine implements insw(,,). There was a time i had the - impression that there would be any difference in error-behaviour. */ -void transport_data(int port, ush * dest, int count) -{ - int i; - ush *d; - for (i = 0, d = dest; i < count; i++, d++) - *d = inw(port); -} -#endif - - -#define MAX_TRIES 100 -static int read_sector(int start) -{ - int tries = 0; - if (cd->background) { - cd->background = 0; - cd->adapter_last = -1; /* invalidate adapter memory */ - stop_read(); - } - cd->fifo_overflowed = 0; - reset_cm260(); /* empty fifo etc. */ - if (start_read(start)) - return -1; - do { - if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) { - debug(("Read timed out sector 0x%x\n", start)); - stats(read_timeout); - stop_read(); - return -3; - } - tries++; - } while (cd->intr_ds & ds_fifo_empty && tries < MAX_TRIES); - if (tries > 1) - debug(("Took me some tries\n")) - else - if (tries == MAX_TRIES) - debug(("MAX_TRIES tries for read sector\n")); - transport_data(r_fifo_output_buffer, cd->sector, - READ_AHEAD * RAW_SECTOR_SIZE / 2); - if (read_background(start + READ_AHEAD, 1)) - stats(read_background); - cd->sector_first = start; - cd->sector_last = start + READ_AHEAD; - stats(read_restarted); - return 0; -} - -/* The function of bottom-half is to send a stop command to the drive - This isn't easy because the routine is not `owned' by any process; - we can't go to sleep! The variable cd->background gives the status: - 0 no read pending - 1 a read is pending - 2 c_stop waits for write_buffer_empty - 3 c_stop waits for receive_buffer_full: echo - 4 c_stop waits for receive_buffer_full: 0xff -*/ - -static void cm206_tasklet_func(unsigned long ignore) -{ - debug(("bh: %d\n", cd->background)); - switch (cd->background) { - case 1: - stats(bh); - if (!(cd->intr_ls & ls_transmitter_buffer_empty)) { - cd->command = c_stop; - outw(dc_mask_sync_error | dc_no_stop_on_error | - (inw(r_data_status) & 0x7f), r_data_control); - cd->background = 2; - break; /* we'd better not time-out here! */ - } else - outw(c_stop, r_uart_transmit); - /* fall into case 2: */ - case 2: - /* the write has been satisfied by interrupt routine */ - cd->background = 3; - break; - case 3: - if (cd->ur_r != cd->ur_w) { - if (cd->ur[cd->ur_r] != c_stop) { - debug(("cm206_bh: c_stop echoed 0x%x\n", - cd->ur[cd->ur_r])); - stats(echo); - } - cd->ur_r++; - cd->ur_r %= UR_SIZE; - } - cd->background++; - break; - case 4: - if (cd->ur_r != cd->ur_w) { - if (cd->ur[cd->ur_r] != 0xff) { - debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->ur[cd->ur_r])); - stats(stop_0xff); - } - cd->ur_r++; - cd->ur_r %= UR_SIZE; - } - cd->background = 0; - } -} - -static DECLARE_TASKLET(cm206_tasklet, cm206_tasklet_func, 0); - -/* This command clears the dsb_possible_media_change flag, so we must - * retain it. - */ -static void get_drive_status(void) -{ - uch status[2]; - type_1_command(c_drive_status, 2, status); /* this might be done faster */ - cd->dsb = status[0]; - cd->cc = status[1]; - cd->media_changed |= - !!(cd->dsb & (dsb_possible_media_change | - dsb_drive_not_ready | dsb_tray_not_closed)); -} - -static void get_disc_status(void) -{ - if (type_1_command(c_disc_status, 7, cd->disc_status)) { - debug(("get_disc_status: error\n")); - } -} - -/* The new open. The real opening strategy is defined in cdrom.c. */ - -static int cm206_open(struct cdrom_device_info *cdi, int purpose) -{ - if (!cd->openfiles) { /* reset only first time */ - cd->background = 0; - reset_cm260(); - cd->adapter_last = -1; /* invalidate adapter memory */ - cd->sector_last = -1; - } - ++cd->openfiles; - stats(open); - return 0; -} - -static void cm206_release(struct cdrom_device_info *cdi) -{ - if (cd->openfiles == 1) { - if (cd->background) { - cd->background = 0; - stop_read(); - } - cd->sector_last = -1; /* Make our internal buffer invalid */ - FIRST_TRACK = 0; /* No valid disc status */ - } - --cd->openfiles; -} - -/* Empty buffer empties $sectors$ sectors of the adapter card buffer, - * and then reads a sector in kernel memory. */ -static void empty_buffer(int sectors) -{ - while (sectors >= 0) { - transport_data(r_fifo_output_buffer, - cd->sector + cd->fifo_overflowed, - RAW_SECTOR_SIZE / 2 - cd->fifo_overflowed); - --sectors; - ++cd->adapter_first; /* update the current adapter sector */ - cd->fifo_overflowed = 0; /* reset overflow bit */ - stats(sector_transferred); - } - cd->sector_first = cd->adapter_first - 1; - cd->sector_last = cd->adapter_first; /* update the buffer sector */ -} - -/* try_adapter. This function determines if the requested sector is - in adapter memory, or will appear there soon. Returns 0 upon - success */ -static int try_adapter(int sector) -{ - if (cd->adapter_first <= sector && sector < cd->adapter_last) { - /* sector is in adapter memory */ - empty_buffer(sector - cd->adapter_first); - return 0; - } else if (cd->background == 1 && cd->adapter_first <= sector - && sector < cd->adapter_first + cd->max_sectors) { - /* a read is going on, we can wait for it */ - cd->wait_back = 1; - while (sector >= cd->adapter_last) { - if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) { - debug(("Timed out during background wait: %d %d %d %d\n", sector, cd->adapter_last, cd->adapter_first, cd->background)); - stats(back_read_timeout); - cd->wait_back = 0; - return -1; - } - } - cd->wait_back = 0; - empty_buffer(sector - cd->adapter_first); - return 0; - } else - return -2; -} - -/* This is not a very smart implementation. We could optimize for - consecutive block numbers. I'm not convinced this would really - bring down the processor load. */ -static void do_cm206_request(request_queue_t * q) -{ - long int i, cd_sec_no; - int quarter, error; - uch *source, *dest; - struct request *req; - - while (1) { /* repeat until all requests have been satisfied */ - req = elv_next_request(q); - if (!req) - return; - - if (req->cmd != READ) { - debug(("Non-read command %d on cdrom\n", req->cmd)); - end_request(req, 0); - continue; - } - spin_unlock_irq(q->queue_lock); - error = 0; - for (i = 0; i < req->nr_sectors; i++) { - int e1, e2; - cd_sec_no = (req->sector + i) / BLOCKS_ISO; /* 4 times 512 bytes */ - quarter = (req->sector + i) % BLOCKS_ISO; - dest = req->buffer + i * LINUX_BLOCK_SIZE; - /* is already in buffer memory? */ - if (cd->sector_first <= cd_sec_no - && cd_sec_no < cd->sector_last) { - source = - ((uch *) cd->sector) + 16 + - quarter * LINUX_BLOCK_SIZE + - (cd_sec_no - - cd->sector_first) * RAW_SECTOR_SIZE; - memcpy(dest, source, LINUX_BLOCK_SIZE); - } else if (!(e1 = try_adapter(cd_sec_no)) || - !(e2 = read_sector(cd_sec_no))) { - source = - ((uch *) cd->sector) + 16 + - quarter * LINUX_BLOCK_SIZE; - memcpy(dest, source, LINUX_BLOCK_SIZE); - } else { - error = 1; - debug(("cm206_request: %d %d\n", e1, e2)); - } - } - spin_lock_irq(q->queue_lock); - end_request(req, !error); - } -} - -/* Audio support. I've tried very hard, but the cm206 drive doesn't - seem to have a get_toc (table-of-contents) function, while i'm - pretty sure it must read the toc upon disc insertion. Therefore - this function has been implemented through a binary search - strategy. All track starts that happen to be found are stored in - cd->toc[], for future use. - - I've spent a whole day on a bug that only shows under Workman--- - I don't get it. Tried everything, nothing works. If workman asks - for track# 0xaa, it'll get the wrong time back. Any other program - receives the correct value. I'm stymied. -*/ - -/* seek seeks to address lba. It does wait to arrive there. */ -static void seek(int lba) -{ - int i; - uch seek_command[4] = { c_seek, }; - - fsm(lba, &seek_command[1]); - for (i = 0; i < 4; i++) - type_0_command(seek_command[i], 0); - cd->dsb = wait_dsb(); -} - -static uch bcdbin(unsigned char bcd) -{ /* stolen from mcd.c! */ - return (bcd >> 4) * 10 + (bcd & 0xf); -} - -static inline uch normalize_track(uch track) -{ - if (track < 1) - return 1; - if (track > LAST_TRACK) - return LAST_TRACK + 1; - return track; -} - -/* This function does a binary search for track start. It records all - * tracks seen in the process. Input $track$ must be between 1 and - * #-of-tracks+1. Note that the start of the disc must be in toc[1].fsm. - */ -static int get_toc_lba(uch track) -{ - int max = 74 * 60 * 75 - 150, min = fsm2lba(cd->toc[1].fsm); - int i, lba, l, old_lba = 0; - uch *q = cd->q; - uch ct; /* current track */ - int binary = 0; - const int skip = 3 * 60 * 75; /* 3 minutes */ - - for (i = track; i > 0; i--) - if (cd->toc[i].track) { - min = fsm2lba(cd->toc[i].fsm); - break; - } - lba = min + skip; - do { - seek(lba); - type_1_command(c_read_current_q, 10, q); - ct = normalize_track(q[1]); - if (!cd->toc[ct].track) { - l = q[9] - bcdbin(q[5]) + 75 * (q[8] - - bcdbin(q[4]) - 2 + - 60 * (q[7] - - bcdbin(q - [3]))); - cd->toc[ct].track = q[1]; /* lead out still 0xaa */ - fsm(l, cd->toc[ct].fsm); - cd->toc[ct].q0 = q[0]; /* contains adr and ctrl info */ - if (ct == track) - return l; - } - old_lba = lba; - if (binary) { - if (ct < track) - min = lba; - else - max = lba; - lba = (min + max) / 2; - } else { - if (ct < track) - lba += skip; - else { - binary = 1; - max = lba; - min = lba - skip; - lba = (min + max) / 2; - } - } - } while (lba != old_lba); - return lba; -} - -static void update_toc_entry(uch track) -{ - track = normalize_track(track); - if (!cd->toc[track].track) - get_toc_lba(track); -} - -/* return 0 upon success */ -static int read_toc_header(struct cdrom_tochdr *hp) -{ - if (!FIRST_TRACK) - get_disc_status(); - if (hp) { - int i; - hp->cdth_trk0 = FIRST_TRACK; - hp->cdth_trk1 = LAST_TRACK; - /* fill in first track position */ - for (i = 0; i < 3; i++) - cd->toc[1].fsm[i] = cd->disc_status[3 + i]; - update_toc_entry(LAST_TRACK + 1); /* find most entries */ - return 0; - } - return -1; -} - -static void play_from_to_msf(struct cdrom_msf *msfp) -{ - uch play_command[] = { c_play, - msfp->cdmsf_frame0, msfp->cdmsf_sec0, msfp->cdmsf_min0, - msfp->cdmsf_frame1, msfp->cdmsf_sec1, msfp->cdmsf_min1, 2, - 2 - }; - int i; - for (i = 0; i < 9; i++) - type_0_command(play_command[i], 0); - for (i = 0; i < 3; i++) - PLAY_TO.fsm[i] = play_command[i + 4]; - PLAY_TO.track = 0; /* say no track end */ - cd->dsb = wait_dsb(); -} - -static void play_from_to_track(int from, int to) -{ - uch play_command[8] = { c_play, }; - int i; - - if (from == 0) { /* continue paused play */ - for (i = 0; i < 3; i++) { - play_command[i + 1] = cd->audio_status[i + 2]; - play_command[i + 4] = PLAY_TO.fsm[i]; - } - } else { - update_toc_entry(from); - update_toc_entry(to + 1); - for (i = 0; i < 3; i++) { - play_command[i + 1] = cd->toc[from].fsm[i]; - PLAY_TO.fsm[i] = play_command[i + 4] = - cd->toc[to + 1].fsm[i]; - } - PLAY_TO.track = to; - } - for (i = 0; i < 7; i++) - type_0_command(play_command[i], 0); - for (i = 0; i < 2; i++) - type_0_command(0x2, 0); /* volume */ - cd->dsb = wait_dsb(); -} - -static int get_current_q(struct cdrom_subchnl *qp) -{ - int i; - uch *q = cd->q; - if (type_1_command(c_read_current_q, 10, q)) - return 0; -/* q[0] = bcdbin(q[0]); Don't think so! */ - for (i = 2; i < 6; i++) - q[i] = bcdbin(q[i]); - qp->cdsc_adr = q[0] & 0xf; - qp->cdsc_ctrl = q[0] >> 4; /* from mcd.c */ - qp->cdsc_trk = q[1]; - qp->cdsc_ind = q[2]; - if (qp->cdsc_format == CDROM_MSF) { - qp->cdsc_reladdr.msf.minute = q[3]; - qp->cdsc_reladdr.msf.second = q[4]; - qp->cdsc_reladdr.msf.frame = q[5]; - qp->cdsc_absaddr.msf.minute = q[7]; - qp->cdsc_absaddr.msf.second = q[8]; - qp->cdsc_absaddr.msf.frame = q[9]; - } else { - qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]); - qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]); - } - get_drive_status(); - if (cd->dsb & dsb_play_in_progress) - qp->cdsc_audiostatus = CDROM_AUDIO_PLAY; - else if (PAUSED) - qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED; - else - qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS; - return 0; -} - -static void invalidate_toc(void) -{ - memset(cd->toc, 0, sizeof(cd->toc)); - memset(cd->disc_status, 0, sizeof(cd->disc_status)); -} - -/* cdrom.c guarantees that cdte_format == CDROM_MSF */ -static void get_toc_entry(struct cdrom_tocentry *ep) -{ - uch track = normalize_track(ep->cdte_track); - update_toc_entry(track); - ep->cdte_addr.msf.frame = cd->toc[track].fsm[0]; - ep->cdte_addr.msf.second = cd->toc[track].fsm[1]; - ep->cdte_addr.msf.minute = cd->toc[track].fsm[2]; - ep->cdte_adr = cd->toc[track].q0 & 0xf; - ep->cdte_ctrl = cd->toc[track].q0 >> 4; - ep->cdte_datamode = 0; -} - -/* Audio ioctl. Ioctl commands connected to audio are in such an - * idiosyncratic i/o format, that we leave these untouched. Return 0 - * upon success. Memory checking has been done by cdrom_ioctl(), the - * calling function, as well as LBA/MSF sanitization. -*/ -static int cm206_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, - void *arg) -{ - switch (cmd) { - case CDROMREADTOCHDR: - return read_toc_header((struct cdrom_tochdr *) arg); - case CDROMREADTOCENTRY: - get_toc_entry((struct cdrom_tocentry *) arg); - return 0; - case CDROMPLAYMSF: - play_from_to_msf((struct cdrom_msf *) arg); - return 0; - case CDROMPLAYTRKIND: /* admittedly, not particularly beautiful */ - play_from_to_track(((struct cdrom_ti *) arg)->cdti_trk0, - ((struct cdrom_ti *) arg)->cdti_trk1); - return 0; - case CDROMSTOP: - PAUSED = 0; - if (cd->dsb & dsb_play_in_progress) - return type_0_command(c_stop, 1); - else - return 0; - case CDROMPAUSE: - get_drive_status(); - if (cd->dsb & dsb_play_in_progress) { - type_0_command(c_stop, 1); - type_1_command(c_audio_status, 5, - cd->audio_status); - PAUSED = 1; /* say we're paused */ - } - return 0; - case CDROMRESUME: - if (PAUSED) - play_from_to_track(0, 0); - PAUSED = 0; - return 0; - case CDROMSTART: - case CDROMVOLCTRL: - return 0; - case CDROMSUBCHNL: - return get_current_q((struct cdrom_subchnl *) arg); - default: - return -EINVAL; - } -} - -static int cm206_media_changed(struct cdrom_device_info *cdi, int disc_nr) -{ - if (cd != NULL) { - int r; - get_drive_status(); /* ensure cd->media_changed OK */ - r = cd->media_changed; - cd->media_changed = 0; /* clear bit */ - return r; - } else - return -EIO; -} - -/* The new generic cdrom support. Routines should be concise, most of - the logic should be in cdrom.c */ - - -/* controls tray movement */ -static int cm206_tray_move(struct cdrom_device_info *cdi, int position) -{ - if (position) { /* 1: eject */ - type_0_command(c_open_tray, 1); - invalidate_toc(); - } else - type_0_command(c_close_tray, 1); /* 0: close */ - return 0; -} - -/* gives current state of the drive */ -static int cm206_drive_status(struct cdrom_device_info *cdi, int slot_nr) -{ - get_drive_status(); - if (cd->dsb & dsb_tray_not_closed) - return CDS_TRAY_OPEN; - if (!(cd->dsb & dsb_disc_present)) - return CDS_NO_DISC; - if (cd->dsb & dsb_drive_not_ready) - return CDS_DRIVE_NOT_READY; - return CDS_DISC_OK; -} - -/* locks or unlocks door lock==1: lock; return 0 upon success */ -static int cm206_lock_door(struct cdrom_device_info *cdi, int lock) -{ - uch command = (lock) ? c_lock_tray : c_unlock_tray; - type_0_command(command, 1); /* wait and get dsb */ - /* the logic calculates the success, 0 means successful */ - return lock ^ ((cd->dsb & dsb_tray_locked) != 0); -} - -/* Although a session start should be in LBA format, we return it in - MSF format because it is slightly easier, and the new generic ioctl - will take care of the necessary conversion. */ -static int cm206_get_last_session(struct cdrom_device_info *cdi, - struct cdrom_multisession *mssp) -{ - if (!FIRST_TRACK) - get_disc_status(); - if (mssp != NULL) { - if (DISC_STATUS & cds_multi_session) { /* multi-session */ - mssp->addr.msf.frame = cd->disc_status[3]; - mssp->addr.msf.second = cd->disc_status[4]; - mssp->addr.msf.minute = cd->disc_status[5]; - mssp->addr_format = CDROM_MSF; - mssp->xa_flag = 1; - } else { - mssp->xa_flag = 0; - } - return 1; - } - return 0; -} - -static int cm206_get_upc(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) -{ - uch upc[10]; - char *ret = mcn->medium_catalog_number; - int i; - - if (type_1_command(c_read_upc, 10, upc)) - return -EIO; - for (i = 0; i < 13; i++) { - int w = i / 2 + 1, r = i % 2; - if (r) - ret[i] = 0x30 | (upc[w] & 0x0f); - else - ret[i] = 0x30 | ((upc[w] >> 4) & 0x0f); - } - ret[13] = '\0'; - return 0; -} - -static int cm206_reset(struct cdrom_device_info *cdi) -{ - stop_read(); - reset_cm260(); - outw(dc_normal | dc_break | READ_AHEAD, r_data_control); - mdelay(1); /* 750 musec minimum */ - outw(dc_normal | READ_AHEAD, r_data_control); - cd->sector_last = -1; /* flag no data buffered */ - cd->adapter_last = -1; - invalidate_toc(); - return 0; -} - -static int cm206_select_speed(struct cdrom_device_info *cdi, int speed) -{ - int r; - switch (speed) { - case 0: - r = type_0_command(c_auto_mode, 1); - break; - case 1: - r = type_0_command(c_force_1x, 1); - break; - case 2: - r = type_0_command(c_force_2x, 1); - break; - default: - return -1; - } - if (r < 0) - return r; - else - return 1; -} - -static struct cdrom_device_ops cm206_dops = { - .open = cm206_open, - .release = cm206_release, - .drive_status = cm206_drive_status, - .media_changed = cm206_media_changed, - .tray_move = cm206_tray_move, - .lock_door = cm206_lock_door, - .select_speed = cm206_select_speed, - .get_last_session = cm206_get_last_session, - .get_mcn = cm206_get_upc, - .reset = cm206_reset, - .audio_ioctl = cm206_audio_ioctl, - .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | - CDC_MULTI_SESSION | CDC_MEDIA_CHANGED | - CDC_MCN | CDC_PLAY_AUDIO | CDC_SELECT_SPEED | - CDC_DRIVE_STATUS, - .n_minors = 1, -}; - - -static struct cdrom_device_info cm206_info = { - .ops = &cm206_dops, - .speed = 2, - .capacity = 1, - .name = "cm206", -}; - -static int cm206_block_open(struct inode *inode, struct file *file) -{ - return cdrom_open(&cm206_info, inode, file); -} - -static int cm206_block_release(struct inode *inode, struct file *file) -{ - return cdrom_release(&cm206_info, file); -} - -static int cm206_block_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) -{ - switch (cmd) { -#ifdef STATISTICS - case CM206CTL_GET_STAT: - if (arg >= NR_STATS) - return -EINVAL; - return cd->stats[arg]; - case CM206CTL_GET_LAST_STAT: - if (arg >= NR_STATS) - return -EINVAL; - return cd->last_stat[arg]; -#endif - default: - break; - } - - return cdrom_ioctl(file, &cm206_info, inode, cmd, arg); -} - -static int cm206_block_media_changed(struct gendisk *disk) -{ - return cdrom_media_changed(&cm206_info); -} - -static struct block_device_operations cm206_bdops = -{ - .owner = THIS_MODULE, - .open = cm206_block_open, - .release = cm206_block_release, - .ioctl = cm206_block_ioctl, - .media_changed = cm206_block_media_changed, -}; - -static struct gendisk *cm206_gendisk; - -/* This function probes for the adapter card. It returns the base - address if it has found the adapter card. One can specify a base - port to probe specifically, or 0 which means span all possible - bases. - - Linus says it is too dangerous to use writes for probing, so we - stick with pure reads for a while. Hope that 8 possible ranges, - request_region, 15 bits of one port and 6 of another make things - likely enough to accept the region on the first hit... - */ -static int __init probe_base_port(int base) -{ - int b = 0x300, e = 0x370; /* this is the range of start addresses */ - volatile int fool, i; - - if (base) - b = e = base; - for (base = b; base <= e; base += 0x10) { - if (!request_region(base, 0x10,"cm206")) - continue; - for (i = 0; i < 3; i++) - fool = inw(base + 2); /* empty possibly uart_receive_buffer */ - if ((inw(base + 6) & 0xffef) != 0x0001 || /* line_status */ - (inw(base) & 0xad00) != 0) { /* data status */ - release_region(base,0x10); - continue; - } - return (base); - } - return 0; -} - -#if !defined(MODULE) || defined(AUTO_PROBE_MODULE) -/* Probe for irq# nr. If nr==0, probe for all possible irq's. */ -static int __init probe_irq(int nr) -{ - int irqs, irq; - outw(dc_normal | READ_AHEAD, r_data_control); /* disable irq-generation */ - sti(); - irqs = probe_irq_on(); - reset_cm260(); /* causes interrupt */ - udelay(100); /* wait for it */ - irq = probe_irq_off(irqs); - outw(dc_normal | READ_AHEAD, r_data_control); /* services interrupt */ - if (nr && irq != nr && irq > 0) - return 0; /* wrong interrupt happened */ - else - return irq; -} -#endif - -int __init cm206_init(void) -{ - uch e = 0; - long int size = sizeof(struct cm206_struct); - struct gendisk *disk; - - printk(KERN_INFO "cm206 cdrom driver " REVISION); - cm206_base = probe_base_port(auto_probe ? 0 : cm206_base); - if (!cm206_base) { - printk(" can't find adapter!\n"); - return -EIO; - } - printk(" adapter at 0x%x", cm206_base); - cd = kmalloc(size, GFP_KERNEL); - if (!cd) - goto out_base; - /* Now we have found the adaptor card, try to reset it. As we have - * found out earlier, this process generates an interrupt as well, - * so we might just exploit that fact for irq probing! */ -#if !defined(MODULE) || defined(AUTO_PROBE_MODULE) - cm206_irq = probe_irq(auto_probe ? 0 : cm206_irq); - if (cm206_irq <= 0) { - printk("can't find IRQ!\n"); - goto out_probe; - } else - printk(" IRQ %d found\n", cm206_irq); -#else - cli(); - reset_cm260(); - /* Now, the problem here is that reset_cm260 can generate an - interrupt. It seems that this can cause a kernel oops some time - later. So we wait a while and `service' this interrupt. */ - mdelay(1); - outw(dc_normal | READ_AHEAD, r_data_control); - sti(); - printk(" using IRQ %d\n", cm206_irq); -#endif - if (send_receive_polled(c_drive_configuration) != - c_drive_configuration) { - printk(KERN_INFO " drive not there\n"); - goto out_probe; - } - e = send_receive_polled(c_gimme); - printk(KERN_INFO "Firmware revision %d", e & dcf_revision_code); - if (e & dcf_transfer_rate) - printk(" double"); - else - printk(" single"); - printk(" speed drive"); - if (e & dcf_motorized_tray) - printk(", motorized tray"); - if (request_irq(cm206_irq, cm206_interrupt, 0, "cm206", NULL)) { - printk("\nUnable to reserve IRQ---aborted\n"); - goto out_probe; - } - printk(".\n"); - - if (register_blkdev(MAJOR_NR, "cm206")) - goto out_blkdev; - - disk = alloc_disk(1); - if (!disk) - goto out_disk; - disk->major = MAJOR_NR; - disk->first_minor = 0; - sprintf(disk->disk_name, "cm206cd"); - disk->fops = &cm206_bdops; - disk->flags = GENHD_FL_CD; - cm206_gendisk = disk; - if (register_cdrom(&cm206_info) != 0) { - printk(KERN_INFO "Cannot register for cdrom %d!\n", MAJOR_NR); - goto out_cdrom; - } - cm206_queue = blk_init_queue(do_cm206_request, &cm206_lock); - if (!cm206_queue) - goto out_queue; - - blk_queue_hardsect_size(cm206_queue, 2048); - disk->queue = cm206_queue; - add_disk(disk); - - memset(cd, 0, sizeof(*cd)); /* give'm some reasonable value */ - cd->sector_last = -1; /* flag no data buffered */ - cd->adapter_last = -1; - init_timer(&cd->timer); - cd->timer.function = cm206_timeout; - cd->max_sectors = (inw(r_data_status) & ds_ram_size) ? 24 : 97; - printk(KERN_INFO "%d kB adapter memory available, " - " %ld bytes kernel memory used.\n", cd->max_sectors * 2, - size); - return 0; - -out_queue: - unregister_cdrom(&cm206_info); -out_cdrom: - put_disk(disk); -out_disk: - unregister_blkdev(MAJOR_NR, "cm206"); -out_blkdev: - free_irq(cm206_irq, NULL); -out_probe: - kfree(cd); -out_base: - release_region(cm206_base, 16); - return -EIO; -} - -#ifdef MODULE - - -static void __init parse_options(void) -{ - int i; - for (i = 0; i < 2; i++) { - if (0x300 <= cm206[i] && i <= 0x370 - && cm206[i] % 0x10 == 0) { - cm206_base = cm206[i]; - auto_probe = 0; - } else if (3 <= cm206[i] && cm206[i] <= 15) { - cm206_irq = cm206[i]; - auto_probe = 0; - } - } -} - -static int __init __cm206_init(void) -{ - parse_options(); -#if !defined(AUTO_PROBE_MODULE) - auto_probe = 0; -#endif - return cm206_init(); -} - -static void __exit cm206_exit(void) -{ - del_gendisk(cm206_gendisk); - put_disk(cm206_gendisk); - if (unregister_cdrom(&cm206_info)) { - printk("Can't unregister cdrom cm206\n"); - return; - } - if (unregister_blkdev(MAJOR_NR, "cm206")) { - printk("Can't unregister major cm206\n"); - return; - } - blk_cleanup_queue(cm206_queue); - free_irq(cm206_irq, NULL); - kfree(cd); - release_region(cm206_base, 16); - printk(KERN_INFO "cm206 removed\n"); -} - -module_init(__cm206_init); -module_exit(cm206_exit); - -#else /* !MODULE */ - -/* This setup function accepts either `auto' or numbers in the range - * 3--11 (for irq) or 0x300--0x370 (for base port) or both. */ - -static int __init cm206_setup(char *s) -{ - int i, p[4]; - - (void) get_options(s, ARRAY_SIZE(p), p); - - if (!strcmp(s, "auto")) - auto_probe = 1; - for (i = 1; i <= p[0]; i++) { - if (0x300 <= p[i] && i <= 0x370 && p[i] % 0x10 == 0) { - cm206_base = p[i]; - auto_probe = 0; - } else if (3 <= p[i] && p[i] <= 15) { - cm206_irq = p[i]; - auto_probe = 0; - } - } - return 1; -} - -__setup("cm206=", cm206_setup); - -#endif /* !MODULE */ -MODULE_ALIAS_BLOCKDEV_MAJOR(CM206_CDROM_MAJOR); - diff --git a/drivers/cdrom/cm206.h b/drivers/cdrom/cm206.h deleted file mode 100644 index 0ae51c1a0da..00000000000 --- a/drivers/cdrom/cm206.h +++ /dev/null @@ -1,171 +0,0 @@ -/* cm206.h Header file for cm206.c. - Copyright (c) 1995 David van Leeuwen -*/ - -#ifndef LINUX_CM206_H -#define LINUX_CM206_H - -#include <linux/ioctl.h> - -/* First, the cm260 stuff */ -/* The ports and irq used. Although CM206_BASE and CM206_IRQ are defined - below, the values are not used unless autoprobing is turned off and - no LILO boot options or module command line options are given. Change - these values to your own as last resort if autoprobing and options - don't work. */ - -#define CM206_BASE 0x340 -#define CM206_IRQ 11 - -#define r_data_status (cm206_base) -#define r_uart_receive (cm206_base+0x2) -#define r_fifo_output_buffer (cm206_base+0x4) -#define r_line_status (cm206_base+0x6) -#define r_data_control (cm206_base+0x8) -#define r_uart_transmit (cm206_base+0xa) -#define r_test_clock (cm206_base+0xc) -#define r_test_control (cm206_base+0xe) - -/* the data_status flags */ -#define ds_ram_size 0x4000 -#define ds_toc_ready 0x2000 -#define ds_fifo_empty 0x1000 -#define ds_sync_error 0x800 -#define ds_crc_error 0x400 -#define ds_data_error 0x200 -#define ds_fifo_overflow 0x100 -#define ds_data_ready 0x80 - -/* the line_status flags */ -#define ls_attention 0x10 -#define ls_parity_error 0x8 -#define ls_overrun 0x4 -#define ls_receive_buffer_full 0x2 -#define ls_transmitter_buffer_empty 0x1 - -/* the data control register flags */ -#define dc_read_q_channel 0x4000 -#define dc_mask_sync_error 0x2000 -#define dc_toc_enable 0x1000 -#define dc_no_stop_on_error 0x800 -#define dc_break 0x400 -#define dc_initialize 0x200 -#define dc_mask_transmit_ready 0x100 -#define dc_flag_enable 0x80 - -/* Define the default data control register flags here */ -#define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \ - dc_mask_transmit_ready) - -/* now some constants related to the cm206 */ -/* another drive status byte, echoed by the cm206 on most commands */ - -#define dsb_error_condition 0x1 -#define dsb_play_in_progress 0x4 -#define dsb_possible_media_change 0x8 -#define dsb_disc_present 0x10 -#define dsb_drive_not_ready 0x20 -#define dsb_tray_locked 0x40 -#define dsb_tray_not_closed 0x80 - -#define dsb_not_useful (dsb_drive_not_ready | dsb_tray_not_closed) - -/* the cm206 command set */ - -#define c_close_tray 0 -#define c_lock_tray 0x01 -#define c_unlock_tray 0x04 -#define c_open_tray 0x05 -#define c_seek 0x10 -#define c_read_data 0x20 -#define c_force_1x 0x21 -#define c_force_2x 0x22 -#define c_auto_mode 0x23 -#define c_play 0x30 -#define c_set_audio_mode 0x31 -#define c_read_current_q 0x41 -#define c_stream_q 0x42 -#define c_drive_status 0x50 -#define c_disc_status 0x51 -#define c_audio_status 0x52 -#define c_drive_configuration 0x53 -#define c_read_upc 0x60 -#define c_stop 0x70 -#define c_calc_checksum 0xe5 - -#define c_gimme 0xf8 - -/* finally, the (error) condition that the drive can be in * - * OK, this is not always an error, but let's prefix it with e_ */ - -#define e_none 0 -#define e_illegal_command 0x01 -#define e_sync 0x02 -#define e_seek 0x03 -#define e_parity 0x04 -#define e_focus 0x05 -#define e_header_sync 0x06 -#define e_code_incompatibility 0x07 -#define e_reset_done 0x08 -#define e_bad_parameter 0x09 -#define e_radial 0x0a -#define e_sub_code 0x0b -#define e_no_data_track 0x0c -#define e_scan 0x0d -#define e_tray_open 0x0f -#define e_no_disc 0x10 -#define e_tray stalled 0x11 - -/* drive configuration masks */ - -#define dcf_revision_code 0x7 -#define dcf_transfer_rate 0x60 -#define dcf_motorized_tray 0x80 - -/* disc status byte */ - -#define cds_multi_session 0x2 -#define cds_all_audio 0x8 -#define cds_xa_mode 0xf0 - -/* finally some ioctls for the driver */ - -#define CM206CTL_GET_STAT _IO( 0x20, 0 ) -#define CM206CTL_GET_LAST_STAT _IO( 0x20, 1 ) - -#ifdef STATISTICS - -/* This is an ugly way to guarantee that the names of the statistics - * are the same in the code and in the diagnostics program. */ - -#ifdef __KERNEL__ -#define x(a) st_ ## a -#define y enum -#else -#define x(a) #a -#define y char * stats_name[] = -#endif - -y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error), - x(crc_error), x(sync_error), x(lost_intr), x(echo), - x(write_timeout), x(receive_timeout), x(read_timeout), - x(dsb_timeout), x(stop_0xff), x(back_read_timeout), - x(sector_transferred), x(read_restarted), x(read_background), - x(bh), x(open), x(ioctl_multisession), x(attention) -#ifdef __KERNEL__ - , x(last_entry) -#endif - }; - -#ifdef __KERNEL__ -#define NR_STATS st_last_entry -#else -#define NR_STATS (sizeof(stats_name)/sizeof(char*)) -#endif - -#undef y -#undef x - -#endif /* STATISTICS */ - -#endif /* LINUX_CM206_H */ diff --git a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c deleted file mode 100644 index b3ab6e9b8df..00000000000 --- a/drivers/cdrom/gscd.c +++ /dev/null @@ -1,1029 +0,0 @@ -#define GSCD_VERSION "0.4a Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>" - -/* - linux/drivers/block/gscd.c - GoldStar R420 CDROM driver - - Copyright (C) 1995 Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de> - based upon pre-works by Eberhard Moenkeberg <emoenke@gwdg.de> - - - For all kind of other information about the GoldStar CDROM - and this Linux device driver I installed a WWW-URL: - http://linux.rz.fh-hannover.de/~raupach - - - If you are the editor of a Linux CD, you should - enable gscd.c within your boot floppy kernel and - send me one of your CDs for free. - - - -------------------------------------------------------------------- - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - 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. - - -------------------------------------------------------------------- - - 9 November 1999 -- Make kernel-parameter implementation work with 2.3.x - Removed init_module & cleanup_module in favor of - module_init & module_exit. - Torben Mathiasen <tmm@image.dk> - -*/ - -/* These settings are for various debug-level. Leave they untouched ... */ -#define NO_GSCD_DEBUG -#define NO_IOCTL_DEBUG -#define NO_MODULE_DEBUG -#define NO_FUTURE_WORK -/*------------------------*/ - -#include <linux/module.h> - -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/kernel.h> -#include <linux/cdrom.h> -#include <linux/ioport.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/init.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/uaccess.h> - -#define MAJOR_NR GOLDSTAR_CDROM_MAJOR -#include <linux/blkdev.h> -#include "gscd.h" - -static int gscdPresent = 0; - -static unsigned char gscd_buf[2048]; /* buffer for block size conversion */ -static int gscd_bn = -1; -static short gscd_port = GSCD_BASE_ADDR; -module_param_named(gscd, gscd_port, short, 0); - -/* Kommt spaeter vielleicht noch mal dran ... - * static DECLARE_WAIT_QUEUE_HEAD(gscd_waitq); - */ - -static void gscd_read_cmd(struct request *req); -static void gscd_hsg2msf(long hsg, struct msf *msf); -static void gscd_bin2bcd(unsigned char *p); - -/* Schnittstellen zum Kern/FS */ - -static void __do_gscd_request(unsigned long dummy); -static int gscd_ioctl(struct inode *, struct file *, unsigned int, - unsigned long); -static int gscd_open(struct inode *, struct file *); -static int gscd_release(struct inode *, struct file *); -static int check_gscd_med_chg(struct gendisk *disk); - -/* GoldStar Funktionen */ - -static void cmd_out(int, char *, char *, int); -static void cmd_status(void); -static void init_cd_drive(int); - -static int get_status(void); -static void clear_Audio(void); -static void cc_invalidate(void); - -/* some things for the next version */ -#ifdef FUTURE_WORK -static void update_state(void); -static long gscd_msf2hsg(struct msf *mp); -static int gscd_bcd2bin(unsigned char bcd); -#endif - - -/* lo-level cmd-Funktionen */ - -static void cmd_info_in(char *, int); -static void cmd_end(void); -static void cmd_read_b(char *, int, int); -static void cmd_read_w(char *, int, int); -static int cmd_unit_alive(void); -static void cmd_write_cmd(char *); - - -/* GoldStar Variablen */ - -static int curr_drv_state; -static int drv_states[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -static int drv_mode; -static int disk_state; -static int speed; -static int ndrives; - -static unsigned char drv_num_read; -static unsigned char f_dsk_valid; -static unsigned char current_drive; -static unsigned char f_drv_ok; - - -static char f_AudioPlay; -static char f_AudioPause; -static int AudioStart_m; -static int AudioStart_f; -static int AudioEnd_m; -static int AudioEnd_f; - -static DEFINE_TIMER(gscd_timer, NULL, 0, 0); -static DEFINE_SPINLOCK(gscd_lock); -static struct request_queue *gscd_queue; - -static struct block_device_operations gscd_fops = { - .owner = THIS_MODULE, - .open = gscd_open, - .release = gscd_release, - .ioctl = gscd_ioctl, - .media_changed = check_gscd_med_chg, -}; - -/* - * Checking if the media has been changed - * (not yet implemented) - */ -static int check_gscd_med_chg(struct gendisk *disk) -{ -#ifdef GSCD_DEBUG - printk("gscd: check_med_change\n"); -#endif - return 0; -} - - -#ifndef MODULE -/* Using new interface for kernel-parameters */ - -static int __init gscd_setup(char *str) -{ - int ints[2]; - (void) get_options(str, ARRAY_SIZE(ints), ints); - - if (ints[0] > 0) { - gscd_port = ints[1]; - } - return 1; -} - -__setup("gscd=", gscd_setup); - -#endif - -static int gscd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, - unsigned long arg) -{ - unsigned char to_do[10]; - unsigned char dummy; - - - switch (cmd) { - case CDROMSTART: /* Spin up the drive */ - /* Don't think we can do this. Even if we could, - * I think the drive times out and stops after a while - * anyway. For now, ignore it. - */ - return 0; - - case CDROMRESUME: /* keine Ahnung was das ist */ - return 0; - - - case CDROMEJECT: - cmd_status(); - to_do[0] = CMD_TRAY_CTL; - cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0); - - return 0; - - default: - return -EINVAL; - } - -} - - -/* - * Take care of the different block sizes between cdrom and Linux. - * When Linux gets variable block sizes this will probably go away. - */ - -static void gscd_transfer(struct request *req) -{ - while (req->nr_sectors > 0 && gscd_bn == req->sector / 4) { - long offs = (req->sector & 3) * 512; - memcpy(req->buffer, gscd_buf + offs, 512); - req->nr_sectors--; - req->sector++; - req->buffer += 512; - } -} - - -/* - * I/O request routine called from Linux kernel. - */ - -static void do_gscd_request(request_queue_t * q) -{ - __do_gscd_request(0); -} - -static void __do_gscd_request(unsigned long dummy) -{ - struct request *req; - unsigned int block; - unsigned int nsect; - -repeat: - req = elv_next_request(gscd_queue); - if (!req) - return; - - block = req->sector; - nsect = req->nr_sectors; - - if (req->sector == -1) - goto out; - - if (req->cmd != READ) { - printk("GSCD: bad cmd %u\n", rq_data_dir(req)); - end_request(req, 0); - goto repeat; - } - - gscd_transfer(req); - - /* if we satisfied the request from the buffer, we're done. */ - - if (req->nr_sectors == 0) { - end_request(req, 1); - goto repeat; - } -#ifdef GSCD_DEBUG - printk("GSCD: block %d, nsect %d\n", block, nsect); -#endif - gscd_read_cmd(req); -out: - return; -} - - - -/* - * Check the result of the set-mode command. On success, send the - * read-data command. - */ - -static void gscd_read_cmd(struct request *req) -{ - long block; - struct gscd_Play_msf gscdcmd; - char cmd[] = { CMD_READ, 0x80, 0, 0, 0, 0, 1 }; /* cmd mode M-S-F secth sectl */ - - cmd_status(); - if (disk_state & (ST_NO_DISK | ST_DOOR_OPEN)) { - printk("GSCD: no disk or door open\n"); - end_request(req, 0); - } else { - if (disk_state & ST_INVALID) { - printk("GSCD: disk invalid\n"); - end_request(req, 0); - } else { - gscd_bn = -1; /* purge our buffer */ - block = req->sector / 4; - gscd_hsg2msf(block, &gscdcmd.start); /* cvt to msf format */ - - cmd[2] = gscdcmd.start.min; - cmd[3] = gscdcmd.start.sec; - cmd[4] = gscdcmd.start.frame; - -#ifdef GSCD_DEBUG - printk("GSCD: read msf %d:%d:%d\n", cmd[2], cmd[3], - cmd[4]); -#endif - cmd_out(TYPE_DATA, (char *) &cmd, - (char *) &gscd_buf[0], 1); - - gscd_bn = req->sector / 4; - gscd_transfer(req); - end_request(req, 1); - } - } - SET_TIMER(__do_gscd_request, 1); -} - - -/* - * Open the device special file. Check that a disk is in. - */ - -static int gscd_open(struct inode *ip, struct file *fp) -{ - int st; - -#ifdef GSCD_DEBUG - printk("GSCD: open\n"); -#endif - - if (gscdPresent == 0) - return -ENXIO; /* no hardware */ - - get_status(); - st = disk_state & (ST_NO_DISK | ST_DOOR_OPEN); - if (st) { - printk("GSCD: no disk or door open\n"); - return -ENXIO; - } - -/* if (updateToc() < 0) - return -EIO; -*/ - - return 0; -} - - -/* - * On close, we flush all gscd blocks from the buffer cache. - */ - -static int gscd_release(struct inode *inode, struct file *file) -{ - -#ifdef GSCD_DEBUG - printk("GSCD: release\n"); -#endif - - gscd_bn = -1; - - return 0; -} - - -static int get_status(void) -{ - int status; - - cmd_status(); - status = disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01); - - if (status == (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) { - cc_invalidate(); - return 1; - } else { - return 0; - } -} - - -static void cc_invalidate(void) -{ - drv_num_read = 0xFF; - f_dsk_valid = 0xFF; - current_drive = 0xFF; - f_drv_ok = 0xFF; - - clear_Audio(); - -} - -static void clear_Audio(void) -{ - - f_AudioPlay = 0; - f_AudioPause = 0; - AudioStart_m = 0; - AudioStart_f = 0; - AudioEnd_m = 0; - AudioEnd_f = 0; - -} - -/* - * waiting ? - */ - -static int wait_drv_ready(void) -{ - int found, read; - - do { - found = inb(GSCDPORT(0)); - found &= 0x0f; - read = inb(GSCDPORT(0)); - read &= 0x0f; - } while (read != found); - -#ifdef GSCD_DEBUG - printk("Wait for: %d\n", read); -#endif - - return read; -} - -static void cc_Ident(char *respons) -{ - char to_do[] = { CMD_IDENT, 0, 0 }; - - cmd_out(TYPE_INFO, (char *) &to_do, (char *) respons, (int) 0x1E); - -} - -static void cc_SetSpeed(void) -{ - char to_do[] = { CMD_SETSPEED, 0, 0 }; - char dummy; - - if (speed > 0) { - to_do[1] = speed & 0x0F; - cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0); - } -} - -static void cc_Reset(void) -{ - char to_do[] = { CMD_RESET, 0 }; - char dummy; - - cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0); -} - -static void cmd_status(void) -{ - char to_do[] = { CMD_STATUS, 0 }; - char dummy; - - cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0); - -#ifdef GSCD_DEBUG - printk("GSCD: Status: %d\n", disk_state); -#endif - -} - -static void cmd_out(int cmd_type, char *cmd, char *respo_buf, int respo_count) -{ - int result; - - - result = wait_drv_ready(); - if (result != drv_mode) { - unsigned long test_loops = 0xFFFF; - int i, dummy; - - outb(curr_drv_state, GSCDPORT(0)); - - /* LOCLOOP_170 */ - do { - result = wait_drv_ready(); - test_loops--; - } while ((result != drv_mode) && (test_loops > 0)); - - if (result != drv_mode) { - disk_state = ST_x08 | ST_x04 | ST_INVALID; - return; - } - - /* ...and waiting */ - for (i = 1, dummy = 1; i < 0xFFFF; i++) { - dummy *= i; - } - } - - /* LOC_172 */ - /* check the unit */ - /* and wake it up */ - if (cmd_unit_alive() != 0x08) { - /* LOC_174 */ - /* game over for this unit */ - disk_state = ST_x08 | ST_x04 | ST_INVALID; - return; - } - - /* LOC_176 */ -#ifdef GSCD_DEBUG - printk("LOC_176 "); -#endif - if (drv_mode == 0x09) { - /* magic... */ - printk("GSCD: magic ...\n"); - outb(result, GSCDPORT(2)); - } - - /* write the command to the drive */ - cmd_write_cmd(cmd); - - /* LOC_178 */ - for (;;) { - result = wait_drv_ready(); - if (result != drv_mode) { - /* LOC_179 */ - if (result == 0x04) { /* Mode 4 */ - /* LOC_205 */ -#ifdef GSCD_DEBUG - printk("LOC_205 "); -#endif - disk_state = inb(GSCDPORT(2)); - - do { - result = wait_drv_ready(); - } while (result != drv_mode); - return; - - } else { - if (result == 0x06) { /* Mode 6 */ - /* LOC_181 */ -#ifdef GSCD_DEBUG - printk("LOC_181 "); -#endif - - if (cmd_type == TYPE_DATA) { - /* read data */ - /* LOC_184 */ - if (drv_mode == 9) { - /* read the data to the buffer (word) */ - - /* (*(cmd+1))?(CD_FRAMESIZE/2):(CD_FRAMESIZE_RAW/2) */ - cmd_read_w - (respo_buf, - respo_count, - CD_FRAMESIZE / - 2); - return; - } else { - /* read the data to the buffer (byte) */ - - /* (*(cmd+1))?(CD_FRAMESIZE):(CD_FRAMESIZE_RAW) */ - cmd_read_b - (respo_buf, - respo_count, - CD_FRAMESIZE); - return; - } - } else { - /* read the info to the buffer */ - cmd_info_in(respo_buf, - respo_count); - return; - } - - return; - } - } - - } else { - disk_state = ST_x08 | ST_x04 | ST_INVALID; - return; - } - } /* for (;;) */ - - -#ifdef GSCD_DEBUG - printk("\n"); -#endif -} - - -static void cmd_write_cmd(char *pstr) -{ - int i, j; - - /* LOC_177 */ -#ifdef GSCD_DEBUG - printk("LOC_177 "); -#endif - - /* calculate the number of parameter */ - j = *pstr & 0x0F; - - /* shift it out */ - for (i = 0; i < j; i++) { - outb(*pstr, GSCDPORT(2)); - pstr++; - } -} - - -static int cmd_unit_alive(void) -{ - int result; - unsigned long max_test_loops; - - - /* LOC_172 */ -#ifdef GSCD_DEBUG - printk("LOC_172 "); -#endif - - outb(curr_drv_state, GSCDPORT(0)); - max_test_loops = 0xFFFF; - - do { - result = wait_drv_ready(); - max_test_loops--; - } while ((result != 0x08) && (max_test_loops > 0)); - - return result; -} - - -static void cmd_info_in(char *pb, int count) -{ - int result; - char read; - - - /* read info */ - /* LOC_182 */ -#ifdef GSCD_DEBUG - printk("LOC_182 "); -#endif - - do { - read = inb(GSCDPORT(2)); - if (count > 0) { - *pb = read; - pb++; - count--; - } - - /* LOC_183 */ - do { - result = wait_drv_ready(); - } while (result == 0x0E); - } while (result == 6); - - cmd_end(); - return; -} - - -static void cmd_read_b(char *pb, int count, int size) -{ - int result; - int i; - - - /* LOC_188 */ - /* LOC_189 */ -#ifdef GSCD_DEBUG - printk("LOC_189 "); -#endif - - do { - do { - result = wait_drv_ready(); - } while (result != 6 || result == 0x0E); - - if (result != 6) { - cmd_end(); - return; - } -#ifdef GSCD_DEBUG - printk("LOC_191 "); -#endif - - for (i = 0; i < size; i++) { - *pb = inb(GSCDPORT(2)); - pb++; - } - count--; - } while (count > 0); - - cmd_end(); - return; -} - - -static void cmd_end(void) -{ - int result; - - - /* LOC_204 */ -#ifdef GSCD_DEBUG - printk("LOC_204 "); -#endif - - do { - result = wait_drv_ready(); - if (result == drv_mode) { - return; - } - } while (result != 4); - - /* LOC_205 */ -#ifdef GSCD_DEBUG - printk("LOC_205 "); -#endif - - disk_state = inb(GSCDPORT(2)); - - do { - result = wait_drv_ready(); - } while (result != drv_mode); - return; - -} - - -static void cmd_read_w(char *pb, int count, int size) -{ - int result; - int i; - - -#ifdef GSCD_DEBUG - printk("LOC_185 "); -#endif - - do { - /* LOC_185 */ - do { - result = wait_drv_ready(); - } while (result != 6 || result == 0x0E); - - if (result != 6) { - cmd_end(); - return; - } - - for (i = 0; i < size; i++) { - /* na, hier muss ich noch mal drueber nachdenken */ - *pb = inw(GSCDPORT(2)); - pb++; - } - count--; - } while (count > 0); - - cmd_end(); - return; -} - -static int __init find_drives(void) -{ - int *pdrv; - int drvnum; - int subdrv; - int i; - - speed = 0; - pdrv = (int *) &drv_states; - curr_drv_state = 0xFE; - subdrv = 0; - drvnum = 0; - - for (i = 0; i < 8; i++) { - subdrv++; - cmd_status(); - disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01; - if (disk_state != (ST_x08 | ST_x04 | ST_INVALID)) { - /* LOC_240 */ - *pdrv = curr_drv_state; - init_cd_drive(drvnum); - pdrv++; - drvnum++; - } else { - if (subdrv < 2) { - continue; - } else { - subdrv = 0; - } - } - -/* curr_drv_state<<1; <-- das geht irgendwie nicht */ -/* muss heissen: curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */ - curr_drv_state *= 2; - curr_drv_state |= 1; -#ifdef GSCD_DEBUG - printk("DriveState: %d\n", curr_drv_state); -#endif - } - - ndrives = drvnum; - return drvnum; -} - -static void __init init_cd_drive(int num) -{ - char resp[50]; - int i; - - printk("GSCD: init unit %d\n", num); - cc_Ident((char *) &resp); - - printk("GSCD: identification: "); - for (i = 0; i < 0x1E; i++) { - printk("%c", resp[i]); - } - printk("\n"); - - cc_SetSpeed(); - -} - -#ifdef FUTURE_WORK -/* return_done */ -static void update_state(void) -{ - unsigned int AX; - - - if ((disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0) { - if (disk_state == (ST_x08 | ST_x04 | ST_INVALID)) { - AX = ST_INVALID; - } - - if ((disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) - == 0) { - invalidate(); - f_drv_ok = 0; - } - - AX |= 0x8000; - } - - if (disk_state & ST_PLAYING) { - AX |= 0x200; - } - - AX |= 0x100; - /* pkt_esbx = AX; */ - - disk_state = 0; - -} -#endif - -static struct gendisk *gscd_disk; - -static void __exit gscd_exit(void) -{ - CLEAR_TIMER; - - del_gendisk(gscd_disk); - put_disk(gscd_disk); - if ((unregister_blkdev(MAJOR_NR, "gscd") == -EINVAL)) { - printk("What's that: can't unregister GoldStar-module\n"); - return; - } - blk_cleanup_queue(gscd_queue); - release_region(gscd_port, GSCD_IO_EXTENT); - printk(KERN_INFO "GoldStar-module released.\n"); -} - -/* This is the common initialisation for the GoldStar drive. */ -/* It is called at boot time AND for module init. */ -static int __init gscd_init(void) -{ - int i; - int result; - int ret=0; - - printk(KERN_INFO "GSCD: version %s\n", GSCD_VERSION); - printk(KERN_INFO - "GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n", - gscd_port); - - if (!request_region(gscd_port, GSCD_IO_EXTENT, "gscd")) { - printk(KERN_WARNING "GSCD: Init failed, I/O port (%X) already" - " in use.\n", gscd_port); - return -EIO; - } - - - /* check for card */ - result = wait_drv_ready(); - if (result == 0x09) { - printk(KERN_WARNING "GSCD: DMA kann ich noch nicht!\n"); - ret = -EIO; - goto err_out1; - } - - if (result == 0x0b) { - drv_mode = result; - i = find_drives(); - if (i == 0) { - printk(KERN_WARNING "GSCD: GoldStar CD-ROM Drive is" - " not found.\n"); - ret = -EIO; - goto err_out1; - } - } - - if ((result != 0x0b) && (result != 0x09)) { - printk(KERN_WARNING "GSCD: GoldStar Interface Adapter does not " - "exist or H/W error\n"); - ret = -EIO; - goto err_out1; - } - - /* reset all drives */ - i = 0; - while (drv_states[i] != 0) { - curr_drv_state = drv_states[i]; - printk(KERN_INFO "GSCD: Reset unit %d ... ", i); - cc_Reset(); - printk("done\n"); - i++; - } - - gscd_disk = alloc_disk(1); - if (!gscd_disk) - goto err_out1; - gscd_disk->major = MAJOR_NR; - gscd_disk->first_minor = 0; - gscd_disk->fops = &gscd_fops; - sprintf(gscd_disk->disk_name, "gscd"); - - if (register_blkdev(MAJOR_NR, "gscd")) { - ret = -EIO; - goto err_out2; - } - - gscd_queue = blk_init_queue(do_gscd_request, &gscd_lock); - if (!gscd_queue) { - ret = -ENOMEM; - goto err_out3; - } - - disk_state = 0; - gscdPresent = 1; - - gscd_disk->queue = gscd_queue; - add_disk(gscd_disk); - - printk(KERN_INFO "GSCD: GoldStar CD-ROM Drive found.\n"); - return 0; - -err_out3: - unregister_blkdev(MAJOR_NR, "gscd"); -err_out2: - put_disk(gscd_disk); -err_out1: - release_region(gscd_port, GSCD_IO_EXTENT); - return ret; -} - -static void gscd_hsg2msf(long hsg, struct msf *msf) -{ - hsg += CD_MSF_OFFSET; - msf->min = hsg / (CD_FRAMES * CD_SECS); - hsg %= CD_FRAMES * CD_SECS; - msf->sec = hsg / CD_FRAMES; - msf->frame = hsg % CD_FRAMES; - - gscd_bin2bcd(&msf->min); /* convert to BCD */ - gscd_bin2bcd(&msf->sec); - gscd_bin2bcd(&msf->frame); -} - - -static void gscd_bin2bcd(unsigned char *p) -{ - int u, t; - - u = *p % 10; - t = *p / 10; - *p = u | (t << 4); -} - - -#ifdef FUTURE_WORK -static long gscd_msf2hsg(struct msf *mp) -{ - return gscd_bcd2bin(mp->frame) - + gscd_bcd2bin(mp->sec) * CD_FRAMES - + gscd_bcd2bin(mp->min) * CD_FRAMES * CD_SECS - CD_MSF_OFFSET; -} - -static int gscd_bcd2bin(unsigned char bcd) -{ - return (bcd >> 4) * 10 + (bcd & 0xF); -} -#endif - -MODULE_AUTHOR("Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>"); -MODULE_LICENSE("GPL"); -module_init(gscd_init); -module_exit(gscd_exit); -MODULE_ALIAS_BLOCKDEV_MAJOR(GOLDSTAR_CDROM_MAJOR); diff --git a/drivers/cdrom/gscd.h b/drivers/cdrom/gscd.h deleted file mode 100644 index a41e64bfc06..00000000000 --- a/drivers/cdrom/gscd.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Definitions for a GoldStar R420 CD-ROM interface - * - * Copyright (C) 1995 Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de> - * Eberhard Moenkeberg <emoenke@gwdg.de> - * - * Published under the GPL. - * - */ - - -/* The Interface Card default address is 0x340. This will work for most - applications. Address selection is accomplished by jumpers PN801-1 to - PN801-4 on the GoldStar Interface Card. - Appropriate settings are: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360 - 0x370, 0x380, 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0 */ - -/* insert here the I/O port address and extent */ -#define GSCD_BASE_ADDR 0x340 -#define GSCD_IO_EXTENT 4 - - -/************** nothing to set up below here *********************/ - -/* port access macro */ -#define GSCDPORT(x) (gscd_port + (x)) - -/* - * commands - * the lower nibble holds the command length - */ -#define CMD_STATUS 0x01 -#define CMD_READSUBQ 0x02 /* 1: ?, 2: UPC, 5: ? */ -#define CMD_SEEK 0x05 /* read_mode M-S-F */ -#define CMD_READ 0x07 /* read_mode M-S-F nsec_h nsec_l */ -#define CMD_RESET 0x11 -#define CMD_SETMODE 0x15 -#define CMD_PLAY 0x17 /* M-S-F M-S-F */ -#define CMD_LOCK_CTL 0x22 /* 0: unlock, 1: lock */ -#define CMD_IDENT 0x31 -#define CMD_SETSPEED 0x32 /* 0: auto */ /* ??? */ -#define CMD_GETMODE 0x41 -#define CMD_PAUSE 0x51 -#define CMD_READTOC 0x61 -#define CMD_DISKINFO 0x71 -#define CMD_TRAY_CTL 0x81 - -/* - * disk_state: - */ -#define ST_PLAYING 0x80 -#define ST_UNLOCKED 0x40 -#define ST_NO_DISK 0x20 -#define ST_DOOR_OPEN 0x10 -#define ST_x08 0x08 -#define ST_x04 0x04 -#define ST_INVALID 0x02 -#define ST_x01 0x01 - -/* - * cmd_type: - */ -#define TYPE_INFO 0x01 -#define TYPE_DATA 0x02 - -/* - * read_mode: - */ -#define MOD_POLLED 0x80 -#define MOD_x08 0x08 -#define MOD_RAW 0x04 - -#define READ_DATA(port, buf, nr) insb(port, buf, nr) - -#define SET_TIMER(func, jifs) \ - ((mod_timer(&gscd_timer, jiffies + jifs)), \ - (gscd_timer.function = func)) - -#define CLEAR_TIMER del_timer_sync(&gscd_timer) - -#define MAX_TRACKS 104 - -struct msf { - unsigned char min; - unsigned char sec; - unsigned char frame; -}; - -struct gscd_Play_msf { - struct msf start; - struct msf end; -}; - -struct gscd_DiskInfo { - unsigned char first; - unsigned char last; - struct msf diskLength; - struct msf firstTrack; -}; - -struct gscd_Toc { - unsigned char ctrl_addr; - unsigned char track; - unsigned char pointIndex; - struct msf trackTime; - struct msf diskTime; -}; - diff --git a/drivers/cdrom/isp16.c b/drivers/cdrom/isp16.c deleted file mode 100644 index db0fd9a240e..00000000000 --- a/drivers/cdrom/isp16.c +++ /dev/null @@ -1,374 +0,0 @@ -/* -- ISP16 cdrom detection and configuration - * - * Copyright (c) 1995,1996 Eric van der Maarel <H.T.M.v.d.Maarel@marin.nl> - * - * Version 0.6 - * - * History: - * 0.5 First release. - * Was included in the sjcd and optcd cdrom drivers. - * 0.6 First "stand-alone" version. - * Removed sound configuration. - * Added "module" support. - * - * 9 November 1999 -- Make kernel-parameter implementation work with 2.3.x - * Removed init_module & cleanup_module in favor of - * module_init & module_exit. - * Torben Mathiasen <tmm@image.dk> - * - * 19 June 2004 -- check_region() converted to request_region() - * and return statement cleanups. - * - Jesper Juhl - * - * Detect cdrom interface on ISP16 sound card. - * Configure cdrom interface. - * - * Algorithm for the card with OPTi 82C928 taken - * from the CDSETUP.SYS driver for MSDOS, - * by OPTi Computers, version 2.03. - * Algorithm for the card with OPTi 82C929 as communicated - * to me by Vadim Model and Leo Spiekman. - * - * 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. - * - */ - -#define ISP16_VERSION_MAJOR 0 -#define ISP16_VERSION_MINOR 6 - -#include <linux/module.h> - -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <asm/io.h> -#include "isp16.h" - -static short isp16_detect(void); -static short isp16_c928__detect(void); -static short isp16_c929__detect(void); -static short isp16_cdi_config(int base, u_char drive_type, int irq, - int dma); -static short isp16_type; /* dependent on type of interface card */ -static u_char isp16_ctrl; -static u_short isp16_enable_port; - -static int isp16_cdrom_base = ISP16_CDROM_IO_BASE; -static int isp16_cdrom_irq = ISP16_CDROM_IRQ; -static int isp16_cdrom_dma = ISP16_CDROM_DMA; -static char *isp16_cdrom_type = ISP16_CDROM_TYPE; - -module_param(isp16_cdrom_base, int, 0); -module_param(isp16_cdrom_irq, int, 0); -module_param(isp16_cdrom_dma, int, 0); -module_param(isp16_cdrom_type, charp, 0); - -#define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p)) -#define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p)) - -#ifndef MODULE - -static int -__init isp16_setup(char *str) -{ - int ints[4]; - - (void) get_options(str, ARRAY_SIZE(ints), ints); - if (ints[0] > 0) - isp16_cdrom_base = ints[1]; - if (ints[0] > 1) - isp16_cdrom_irq = ints[2]; - if (ints[0] > 2) - isp16_cdrom_dma = ints[3]; - if (str) - isp16_cdrom_type = str; - - return 1; -} - -__setup("isp16=", isp16_setup); - -#endif /* MODULE */ - -/* - * ISP16 initialisation. - * - */ -static int __init isp16_init(void) -{ - u_char expected_drive; - - printk(KERN_INFO - "ISP16: configuration cdrom interface, version %d.%d.\n", - ISP16_VERSION_MAJOR, ISP16_VERSION_MINOR); - - if (!strcmp(isp16_cdrom_type, "noisp16")) { - printk("ISP16: no cdrom interface configured.\n"); - return 0; - } - - if (!request_region(ISP16_IO_BASE, ISP16_IO_SIZE, "isp16")) { - printk("ISP16: i/o ports already in use.\n"); - goto out; - } - - if ((isp16_type = isp16_detect()) < 0) { - printk("ISP16: no cdrom interface found.\n"); - goto cleanup_out; - } - - printk(KERN_INFO - "ISP16: cdrom interface (with OPTi 82C92%d chip) detected.\n", - (isp16_type == 2) ? 9 : 8); - - if (!strcmp(isp16_cdrom_type, "Sanyo")) - expected_drive = - (isp16_type ? ISP16_SANYO1 : ISP16_SANYO0); - else if (!strcmp(isp16_cdrom_type, "Sony")) - expected_drive = ISP16_SONY; - else if (!strcmp(isp16_cdrom_type, "Panasonic")) - expected_drive = - (isp16_type ? ISP16_PANASONIC1 : ISP16_PANASONIC0); - else if (!strcmp(isp16_cdrom_type, "Mitsumi")) - expected_drive = ISP16_MITSUMI; - else { - printk("ISP16: %s not supported by cdrom interface.\n", - isp16_cdrom_type); - goto cleanup_out; - } - - if (isp16_cdi_config(isp16_cdrom_base, expected_drive, - isp16_cdrom_irq, isp16_cdrom_dma) < 0) { - printk - ("ISP16: cdrom interface has not been properly configured.\n"); - goto cleanup_out; - } - printk(KERN_INFO - "ISP16: cdrom interface set up with io base 0x%03X, irq %d, dma %d," - " type %s.\n", isp16_cdrom_base, isp16_cdrom_irq, - isp16_cdrom_dma, isp16_cdrom_type); - return 0; - -cleanup_out: - release_region(ISP16_IO_BASE, ISP16_IO_SIZE); -out: - return -EIO; -} - -static short __init isp16_detect(void) -{ - - if (isp16_c929__detect() >= 0) - return 2; - else - return (isp16_c928__detect()); -} - -static short __init isp16_c928__detect(void) -{ - u_char ctrl; - u_char enable_cdrom; - u_char io; - short i = -1; - - isp16_ctrl = ISP16_C928__CTRL; - isp16_enable_port = ISP16_C928__ENABLE_PORT; - - /* read' and write' are a special read and write, respectively */ - - /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */ - ctrl = ISP16_IN(ISP16_CTRL_PORT) & 0xFC; - ISP16_OUT(ISP16_CTRL_PORT, ctrl); - - /* read' 3,4 and 5-bit from the cdrom enable port */ - enable_cdrom = ISP16_IN(ISP16_C928__ENABLE_PORT) & 0x38; - - if (!(enable_cdrom & 0x20)) { /* 5-bit not set */ - /* read' last 2 bits of ISP16_IO_SET_PORT */ - io = ISP16_IN(ISP16_IO_SET_PORT) & 0x03; - if (((io & 0x01) << 1) == (io & 0x02)) { /* bits are the same */ - if (io == 0) { /* ...the same and 0 */ - i = 0; - enable_cdrom |= 0x20; - } else { /* ...the same and 1 *//* my card, first time 'round */ - i = 1; - enable_cdrom |= 0x28; - } - ISP16_OUT(ISP16_C928__ENABLE_PORT, enable_cdrom); - } else { /* bits are not the same */ - ISP16_OUT(ISP16_CTRL_PORT, ctrl); - return i; /* -> not detected: possibly incorrect conclusion */ - } - } else if (enable_cdrom == 0x20) - i = 0; - else if (enable_cdrom == 0x28) /* my card, already initialised */ - i = 1; - - ISP16_OUT(ISP16_CTRL_PORT, ctrl); - - return i; -} - -static short __init isp16_c929__detect(void) -{ - u_char ctrl; - u_char tmp; - - isp16_ctrl = ISP16_C929__CTRL; - isp16_enable_port = ISP16_C929__ENABLE_PORT; - - /* read' and write' are a special read and write, respectively */ - - /* read' ISP16_CTRL_PORT and save */ - ctrl = ISP16_IN(ISP16_CTRL_PORT); - - /* write' zero to the ctrl port and get response */ - ISP16_OUT(ISP16_CTRL_PORT, 0); - tmp = ISP16_IN(ISP16_CTRL_PORT); - - if (tmp != 2) /* isp16 with 82C929 not detected */ - return -1; - - /* restore ctrl port value */ - ISP16_OUT(ISP16_CTRL_PORT, ctrl); - - return 2; -} - -static short __init -isp16_cdi_config(int base, u_char drive_type, int irq, int dma) -{ - u_char base_code; - u_char irq_code; - u_char dma_code; - u_char i; - - if ((drive_type == ISP16_MITSUMI) && (dma != 0)) - printk("ISP16: Mitsumi cdrom drive has no dma support.\n"); - - switch (base) { - case 0x340: - base_code = ISP16_BASE_340; - break; - case 0x330: - base_code = ISP16_BASE_330; - break; - case 0x360: - base_code = ISP16_BASE_360; - break; - case 0x320: - base_code = ISP16_BASE_320; - break; - default: - printk - ("ISP16: base address 0x%03X not supported by cdrom interface.\n", - base); - return -1; - } - switch (irq) { - case 0: - irq_code = ISP16_IRQ_X; - break; /* disable irq */ - case 5: - irq_code = ISP16_IRQ_5; - printk("ISP16: irq 5 shouldn't be used by cdrom interface," - " due to possible conflicts with the sound card.\n"); - break; - case 7: - irq_code = ISP16_IRQ_7; - printk("ISP16: irq 7 shouldn't be used by cdrom interface," - " due to possible conflicts with the sound card.\n"); - break; - case 3: - irq_code = ISP16_IRQ_3; - break; - case 9: - irq_code = ISP16_IRQ_9; - break; - case 10: - irq_code = ISP16_IRQ_10; - break; - case 11: - irq_code = ISP16_IRQ_11; - break; - default: - printk("ISP16: irq %d not supported by cdrom interface.\n", - irq); - return -1; - } - switch (dma) { - case 0: - dma_code = ISP16_DMA_X; - break; /* disable dma */ - case 1: - printk("ISP16: dma 1 cannot be used by cdrom interface," - " due to conflict with the sound card.\n"); - return -1; - break; - case 3: - dma_code = ISP16_DMA_3; - break; - case 5: - dma_code = ISP16_DMA_5; - break; - case 6: - dma_code = ISP16_DMA_6; - break; - case 7: - dma_code = ISP16_DMA_7; - break; - default: - printk("ISP16: dma %d not supported by cdrom interface.\n", - dma); - return -1; - } - - if (drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 && - drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 && - drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI && - drive_type != ISP16_DRIVE_X) { - printk - ("ISP16: drive type (code 0x%02X) not supported by cdrom" - " interface.\n", drive_type); - return -1; - } - - /* set type of interface */ - i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK; /* clear some bits */ - ISP16_OUT(ISP16_DRIVE_SET_PORT, i | drive_type); - - /* enable cdrom on interface with 82C929 chip */ - if (isp16_type > 1) - ISP16_OUT(isp16_enable_port, ISP16_ENABLE_CDROM); - - /* set base address, irq and dma */ - i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK; /* keep some bits */ - ISP16_OUT(ISP16_IO_SET_PORT, i | base_code | irq_code | dma_code); - - return 0; -} - -static void __exit isp16_exit(void) -{ - release_region(ISP16_IO_BASE, ISP16_IO_SIZE); - printk(KERN_INFO "ISP16: module released.\n"); -} - -module_init(isp16_init); -module_exit(isp16_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/cdrom/isp16.h b/drivers/cdrom/isp16.h deleted file mode 100644 index 5bd22c8f7a9..00000000000 --- a/drivers/cdrom/isp16.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -- isp16.h - * - * Header for detection and initialisation of cdrom interface (only) on - * ISP16 (MAD16, Mozart) sound card. - * - * 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. - * - */ - -/* These are the default values */ -#define ISP16_CDROM_TYPE "Sanyo" -#define ISP16_CDROM_IO_BASE 0x340 -#define ISP16_CDROM_IRQ 0 -#define ISP16_CDROM_DMA 0 - -/* Some (Media)Magic */ -/* define types of drive the interface on an ISP16 card may be looking at */ -#define ISP16_DRIVE_X 0x00 -#define ISP16_SONY 0x02 -#define ISP16_PANASONIC0 0x02 -#define ISP16_SANYO0 0x02 -#define ISP16_MITSUMI 0x04 -#define ISP16_PANASONIC1 0x06 -#define ISP16_SANYO1 0x06 -#define ISP16_DRIVE_NOT_USED 0x08 /* not used */ -#define ISP16_DRIVE_SET_MASK 0xF1 /* don't change 0-bit or 4-7-bits*/ -/* ...for port */ -#define ISP16_DRIVE_SET_PORT 0xF8D -/* set io parameters */ -#define ISP16_BASE_340 0x00 -#define ISP16_BASE_330 0x40 -#define ISP16_BASE_360 0x80 -#define ISP16_BASE_320 0xC0 -#define ISP16_IRQ_X 0x00 -#define ISP16_IRQ_5 0x04 /* shouldn't be used to avoid sound card conflicts */ -#define ISP16_IRQ_7 0x08 /* shouldn't be used to avoid sound card conflicts */ -#define ISP16_IRQ_3 0x0C -#define ISP16_IRQ_9 0x10 -#define ISP16_IRQ_10 0x14 -#define ISP16_IRQ_11 0x18 -#define ISP16_DMA_X 0x03 -#define ISP16_DMA_3 0x00 -#define ISP16_DMA_5 0x00 -#define ISP16_DMA_6 0x01 -#define ISP16_DMA_7 0x02 -#define ISP16_IO_SET_MASK 0x20 /* don't change 5-bit */ -/* ...for port */ -#define ISP16_IO_SET_PORT 0xF8E -/* enable the card */ -#define ISP16_C928__ENABLE_PORT 0xF90 /* ISP16 with OPTi 82C928 chip */ -#define ISP16_C929__ENABLE_PORT 0xF91 /* ISP16 with OPTi 82C929 chip */ -#define ISP16_ENABLE_CDROM 0x80 /* seven bit */ - -/* the magic stuff */ -#define ISP16_CTRL_PORT 0xF8F -#define ISP16_C928__CTRL 0xE2 /* ISP16 with OPTi 82C928 chip */ -#define ISP16_C929__CTRL 0xE3 /* ISP16 with OPTi 82C929 chip */ - -#define ISP16_IO_BASE 0xF8D -#define ISP16_IO_SIZE 5 /* ports used from 0xF8D up to 0xF91 */ diff --git a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c deleted file mode 100644 index 4310cc84dfe..00000000000 --- a/drivers/cdrom/mcdx.c +++ /dev/null @@ -1,1943 +0,0 @@ -/* - * The Mitsumi CDROM interface - * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de> - * VERSION: 2.14(hs) - * - * ... anyway, I'm back again, thanks to Marcin, he adopted - * large portions of my code (at least the parts containing - * my main thoughts ...) - * - ****************** H E L P ********************************* - * If you ever plan to update your CD ROM drive and perhaps - * want to sell or simply give away your Mitsumi FX-001[DS] - * -- Please -- - * mail me (heiko@lotte.sax.de). When my last drive goes - * ballistic no more driver support will be available from me! - ************************************************************* - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Thanks to - * The Linux Community at all and ... - * Martin Harriss (he wrote the first Mitsumi Driver) - * Eberhard Moenkeberg (he gave me much support and the initial kick) - * Bernd Huebner, Ruediger Helsch (Unifix-Software GmbH, they - * improved the original driver) - * Jon Tombs, Bjorn Ekwall (module support) - * Daniel v. Mosnenck (he sent me the Technical and Programming Reference) - * Gerd Knorr (he lent me his PhotoCD) - * Nils Faerber and Roger E. Wolff (extensively tested the LU portion) - * Andreas Kies (testing the mysterious hang-ups) - * Heiko Eissfeldt (VERIFY_READ/WRITE) - * Marcin Dalecki (improved performance, shortened code) - * ... somebody forgotten? - * - * 9 November 1999 -- Make kernel-parameter implementation work with 2.3.x - * Removed init_module & cleanup_module in favor of - * module_init & module_exit. - * Torben Mathiasen <tmm@image.dk> - */ - - -#ifdef RCS -static const char *mcdx_c_version - = "$Id: mcdx.c,v 1.21 1997/01/26 07:12:59 davem Exp $"; -#endif - -#include <linux/module.h> - -#include <linux/errno.h> -#include <linux/interrupt.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/cdrom.h> -#include <linux/ioport.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <asm/io.h> -#include <asm/current.h> -#include <asm/uaccess.h> - -#include <linux/major.h> -#define MAJOR_NR MITSUMI_X_CDROM_MAJOR -#include <linux/blkdev.h> - -#include "mcdx.h" - -#ifndef HZ -#error HZ not defined -#endif - -#define xwarn(fmt, args...) printk(KERN_WARNING MCDX " " fmt, ## args) - -#if !MCDX_QUIET -#define xinfo(fmt, args...) printk(KERN_INFO MCDX " " fmt, ## args) -#else -#define xinfo(fmt, args...) { ; } -#endif - -#if MCDX_DEBUG -#define xtrace(lvl, fmt, args...) \ - { if (lvl > 0) \ - { printk(KERN_DEBUG MCDX ":: " fmt, ## args); } } -#define xdebug(fmt, args...) printk(KERN_DEBUG MCDX ":: " fmt, ## args) -#else -#define xtrace(lvl, fmt, args...) { ; } -#define xdebug(fmt, args...) { ; } -#endif - -/* CONSTANTS *******************************************************/ - -/* Following are the number of sectors we _request_ from the drive - every time an access outside the already requested range is done. - The _direct_ size is the number of sectors we're allowed to skip - directly (performing a read instead of requesting the new sector - needed */ -static const int REQUEST_SIZE = 800; /* should be less then 255 * 4 */ -static const int DIRECT_SIZE = 400; /* should be less then REQUEST_SIZE */ - -enum drivemodes { TOC, DATA, RAW, COOKED }; -enum datamodes { MODE0, MODE1, MODE2 }; -enum resetmodes { SOFT, HARD }; - -static const int SINGLE = 0x01; /* single speed drive (FX001S, LU) */ -static const int DOUBLE = 0x02; /* double speed drive (FX001D, ..? */ -static const int DOOR = 0x04; /* door locking capability */ -static const int MULTI = 0x08; /* multi session capability */ - -static const unsigned char READ1X = 0xc0; -static const unsigned char READ2X = 0xc1; - - -/* DECLARATIONS ****************************************************/ -struct s_subqcode { - unsigned char control; - unsigned char tno; - unsigned char index; - struct cdrom_msf0 tt; - struct cdrom_msf0 dt; -}; - -struct s_diskinfo { - unsigned int n_first; - unsigned int n_last; - struct cdrom_msf0 msf_leadout; - struct cdrom_msf0 msf_first; -}; - -struct s_multi { - unsigned char multi; - struct cdrom_msf0 msf_last; -}; - -struct s_version { - unsigned char code; - unsigned char ver; -}; - -/* Per drive/controller stuff **************************************/ - -struct s_drive_stuff { - /* waitqueues */ - wait_queue_head_t busyq; - wait_queue_head_t lockq; - wait_queue_head_t sleepq; - - /* flags */ - volatile int introk; /* status of last irq operation */ - volatile int busy; /* drive performs an operation */ - volatile int lock; /* exclusive usage */ - - /* cd infos */ - struct s_diskinfo di; - struct s_multi multi; - struct s_subqcode *toc; /* first entry of the toc array */ - struct s_subqcode start; - struct s_subqcode stop; - int xa; /* 1 if xa disk */ - int audio; /* 1 if audio disk */ - int audiostatus; - - /* `buffer' control */ - volatile int valid; /* pending, ..., values are valid */ - volatile int pending; /* next sector to be read */ - volatile int low_border; /* first sector not to be skipped direct */ - volatile int high_border; /* first sector `out of area' */ -#ifdef AK2 - volatile int int_err; -#endif /* AK2 */ - - /* adds and odds */ - unsigned wreg_data; /* w data */ - unsigned wreg_reset; /* w hardware reset */ - unsigned wreg_hcon; /* w hardware conf */ - unsigned wreg_chn; /* w channel */ - unsigned rreg_data; /* r data */ - unsigned rreg_status; /* r status */ - - int irq; /* irq used by this drive */ - int present; /* drive present and its capabilities */ - unsigned char readcmd; /* read cmd depends on single/double speed */ - unsigned char playcmd; /* play should always be single speed */ - unsigned int xxx; /* set if changed, reset while open */ - unsigned int yyy; /* set if changed, reset by media_changed */ - int users; /* keeps track of open/close */ - int lastsector; /* last block accessible */ - int status; /* last operation's error / status */ - int readerrs; /* # of blocks read w/o error */ - struct cdrom_device_info info; - struct gendisk *disk; -}; - - -/* Prototypes ******************************************************/ - -/* The following prototypes are already declared elsewhere. They are - repeated here to show what's going on. And to sense, if they're - changed elsewhere. */ - -static int mcdx_init(void); - -static int mcdx_block_open(struct inode *inode, struct file *file) -{ - struct s_drive_stuff *p = inode->i_bdev->bd_disk->private_data; - return cdrom_open(&p->info, inode, file); -} - -static int mcdx_block_release(struct inode *inode, struct file *file) -{ - struct s_drive_stuff *p = inode->i_bdev->bd_disk->private_data; - return cdrom_release(&p->info, file); -} - -static int mcdx_block_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) -{ - struct s_drive_stuff *p = inode->i_bdev->bd_disk->private_data; - return cdrom_ioctl(file, &p->info, inode, cmd, arg); -} - -static int mcdx_block_media_changed(struct gendisk *disk) -{ - struct s_drive_stuff *p = disk->private_data; - return cdrom_media_changed(&p->info); -} - -static struct block_device_operations mcdx_bdops = -{ - .owner = THIS_MODULE, - .open = mcdx_block_open, - .release = mcdx_block_release, - .ioctl = mcdx_block_ioctl, - .media_changed = mcdx_block_media_changed, -}; - - -/* Indirect exported functions. These functions are exported by their - addresses, such as mcdx_open and mcdx_close in the - structure mcdx_dops. */ - -/* exported by file_ops */ -static int mcdx_open(struct cdrom_device_info *cdi, int purpose); -static void mcdx_close(struct cdrom_device_info *cdi); -static int mcdx_media_changed(struct cdrom_device_info *cdi, int disc_nr); -static int mcdx_tray_move(struct cdrom_device_info *cdi, int position); -static int mcdx_lockdoor(struct cdrom_device_info *cdi, int lock); -static int mcdx_audio_ioctl(struct cdrom_device_info *cdi, - unsigned int cmd, void *arg); - -/* misc internal support functions */ -static void log2msf(unsigned int, struct cdrom_msf0 *); -static unsigned int msf2log(const struct cdrom_msf0 *); -static unsigned int uint2bcd(unsigned int); -static unsigned int bcd2uint(unsigned char); -static unsigned port(int *); -static int irq(int *); -static void mcdx_delay(struct s_drive_stuff *, long jifs); -static int mcdx_transfer(struct s_drive_stuff *, char *buf, int sector, - int nr_sectors); -static int mcdx_xfer(struct s_drive_stuff *, char *buf, int sector, - int nr_sectors); - -static int mcdx_config(struct s_drive_stuff *, int); -static int mcdx_requestversion(struct s_drive_stuff *, struct s_version *, - int); -static int mcdx_stop(struct s_drive_stuff *, int); -static int mcdx_hold(struct s_drive_stuff *, int); -static int mcdx_reset(struct s_drive_stuff *, enum resetmodes, int); -static int mcdx_setdrivemode(struct s_drive_stuff *, enum drivemodes, int); -static int mcdx_setdatamode(struct s_drive_stuff *, enum datamodes, int); -static int mcdx_requestsubqcode(struct s_drive_stuff *, - struct s_subqcode *, int); -static int mcdx_requestmultidiskinfo(struct s_drive_stuff *, - struct s_multi *, int); -static int mcdx_requesttocdata(struct s_drive_stuff *, struct s_diskinfo *, - int); -static int mcdx_getstatus(struct s_drive_stuff *, int); -static int mcdx_getval(struct s_drive_stuff *, int to, int delay, char *); -static int mcdx_talk(struct s_drive_stuff *, - const unsigned char *cmd, size_t, - void *buffer, size_t size, unsigned int timeout, int); -static int mcdx_readtoc(struct s_drive_stuff *); -static int mcdx_playtrk(struct s_drive_stuff *, const struct cdrom_ti *); -static int mcdx_playmsf(struct s_drive_stuff *, const struct cdrom_msf *); -static int mcdx_setattentuator(struct s_drive_stuff *, - struct cdrom_volctrl *, int); - -/* static variables ************************************************/ - -static int mcdx_drive_map[][2] = MCDX_DRIVEMAP; -static struct s_drive_stuff *mcdx_stuffp[MCDX_NDRIVES]; -static DEFINE_SPINLOCK(mcdx_lock); -static struct request_queue *mcdx_queue; - -/* You can only set the first two pairs, from old MODULE_PARM code. */ -static int mcdx_set(const char *val, struct kernel_param *kp) -{ - get_options((char *)val, 4, (int *)mcdx_drive_map); - return 0; -} -module_param_call(mcdx, mcdx_set, NULL, NULL, 0); - -static struct cdrom_device_ops mcdx_dops = { - .open = mcdx_open, - .release = mcdx_close, - .media_changed = mcdx_media_changed, - .tray_move = mcdx_tray_move, - .lock_door = mcdx_lockdoor, - .audio_ioctl = mcdx_audio_ioctl, - .capability = CDC_OPEN_TRAY | CDC_LOCK | CDC_MEDIA_CHANGED | - CDC_PLAY_AUDIO | CDC_DRIVE_STATUS, -}; - -/* KERNEL INTERFACE FUNCTIONS **************************************/ - - -static int mcdx_audio_ioctl(struct cdrom_device_info *cdi, - unsigned int cmd, void *arg) -{ - struct s_drive_stuff *stuffp = cdi->handle; - - if (!stuffp->present) - return -ENXIO; - - if (stuffp->xxx) { - if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) { - stuffp->lastsector = -1; - } else { - stuffp->lastsector = (CD_FRAMESIZE / 512) - * msf2log(&stuffp->di.msf_leadout) - 1; - } - - if (stuffp->toc) { - kfree(stuffp->toc); - stuffp->toc = NULL; - if (-1 == mcdx_readtoc(stuffp)) - return -1; - } - - stuffp->xxx = 0; - } - - switch (cmd) { - case CDROMSTART:{ - xtrace(IOCTL, "ioctl() START\n"); - /* Spin up the drive. Don't think we can do this. - * For now, ignore it. - */ - return 0; - } - - case CDROMSTOP:{ - xtrace(IOCTL, "ioctl() STOP\n"); - stuffp->audiostatus = CDROM_AUDIO_INVALID; - if (-1 == mcdx_stop(stuffp, 1)) - return -EIO; - return 0; - } - - case CDROMPLAYTRKIND:{ - struct cdrom_ti *ti = (struct cdrom_ti *) arg; - - xtrace(IOCTL, "ioctl() PLAYTRKIND\n"); - if ((ti->cdti_trk0 < stuffp->di.n_first) - || (ti->cdti_trk0 > stuffp->di.n_last) - || (ti->cdti_trk1 < stuffp->di.n_first)) - return -EINVAL; - if (ti->cdti_trk1 > stuffp->di.n_last) - ti->cdti_trk1 = stuffp->di.n_last; - xtrace(PLAYTRK, "ioctl() track %d to %d\n", - ti->cdti_trk0, ti->cdti_trk1); - return mcdx_playtrk(stuffp, ti); - } - - case CDROMPLAYMSF:{ - struct cdrom_msf *msf = (struct cdrom_msf *) arg; - - xtrace(IOCTL, "ioctl() PLAYMSF\n"); - - if ((stuffp->audiostatus == CDROM_AUDIO_PLAY) - && (-1 == mcdx_hold(stuffp, 1))) - return -EIO; - - msf->cdmsf_min0 = uint2bcd(msf->cdmsf_min0); - msf->cdmsf_sec0 = uint2bcd(msf->cdmsf_sec0); - msf->cdmsf_frame0 = uint2bcd(msf->cdmsf_frame0); - - msf->cdmsf_min1 = uint2bcd(msf->cdmsf_min1); - msf->cdmsf_sec1 = uint2bcd(msf->cdmsf_sec1); - msf->cdmsf_frame1 = uint2bcd(msf->cdmsf_frame1); - - stuffp->stop.dt.minute = msf->cdmsf_min1; - stuffp->stop.dt.second = msf->cdmsf_sec1; - stuffp->stop.dt.frame = msf->cdmsf_frame1; - - return mcdx_playmsf(stuffp, msf); - } - - case CDROMRESUME:{ - xtrace(IOCTL, "ioctl() RESUME\n"); - return mcdx_playtrk(stuffp, NULL); - } - - case CDROMREADTOCENTRY:{ - struct cdrom_tocentry *entry = - (struct cdrom_tocentry *) arg; - struct s_subqcode *tp = NULL; - xtrace(IOCTL, "ioctl() READTOCENTRY\n"); - - if (-1 == mcdx_readtoc(stuffp)) - return -1; - if (entry->cdte_track == CDROM_LEADOUT) - tp = &stuffp->toc[stuffp->di.n_last - - stuffp->di.n_first + 1]; - else if (entry->cdte_track > stuffp->di.n_last - || entry->cdte_track < stuffp->di.n_first) - return -EINVAL; - else - tp = &stuffp->toc[entry->cdte_track - - stuffp->di.n_first]; - - if (NULL == tp) - return -EIO; - entry->cdte_adr = tp->control; - entry->cdte_ctrl = tp->control >> 4; - /* Always return stuff in MSF, and let the Uniform cdrom driver - worry about what the user actually wants */ - entry->cdte_addr.msf.minute = - bcd2uint(tp->dt.minute); - entry->cdte_addr.msf.second = - bcd2uint(tp->dt.second); - entry->cdte_addr.msf.frame = - bcd2uint(tp->dt.frame); - return 0; - } - - case CDROMSUBCHNL:{ - struct cdrom_subchnl *sub = - (struct cdrom_subchnl *) arg; - struct s_subqcode q; - - xtrace(IOCTL, "ioctl() SUBCHNL\n"); - - if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) - return -EIO; - - xtrace(SUBCHNL, "audiostatus: %x\n", - stuffp->audiostatus); - sub->cdsc_audiostatus = stuffp->audiostatus; - sub->cdsc_adr = q.control; - sub->cdsc_ctrl = q.control >> 4; - sub->cdsc_trk = bcd2uint(q.tno); - sub->cdsc_ind = bcd2uint(q.index); - - xtrace(SUBCHNL, "trk %d, ind %d\n", - sub->cdsc_trk, sub->cdsc_ind); - /* Always return stuff in MSF, and let the Uniform cdrom driver - worry about what the user actually wants */ - sub->cdsc_absaddr.msf.minute = - bcd2uint(q.dt.minute); - sub->cdsc_absaddr.msf.second = - bcd2uint(q.dt.second); - sub->cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame); - sub->cdsc_reladdr.msf.minute = - bcd2uint(q.tt.minute); - sub->cdsc_reladdr.msf.second = - bcd2uint(q.tt.second); - sub->cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame); - xtrace(SUBCHNL, - "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n", - sub->cdsc_absaddr.msf.minute, - sub->cdsc_absaddr.msf.second, - sub->cdsc_absaddr.msf.frame, - sub->cdsc_reladdr.msf.minute, - sub->cdsc_reladdr.msf.second, - sub->cdsc_reladdr.msf.frame); - - return 0; - } - - case CDROMREADTOCHDR:{ - struct cdrom_tochdr *toc = - (struct cdrom_tochdr *) arg; - - xtrace(IOCTL, "ioctl() READTOCHDR\n"); - toc->cdth_trk0 = stuffp->di.n_first; - toc->cdth_trk1 = stuffp->di.n_last; - xtrace(TOCHDR, - "ioctl() track0 = %d, track1 = %d\n", - stuffp->di.n_first, stuffp->di.n_last); - return 0; - } - - case CDROMPAUSE:{ - xtrace(IOCTL, "ioctl() PAUSE\n"); - if (stuffp->audiostatus != CDROM_AUDIO_PLAY) - return -EINVAL; - if (-1 == mcdx_stop(stuffp, 1)) - return -EIO; - stuffp->audiostatus = CDROM_AUDIO_PAUSED; - if (-1 == - mcdx_requestsubqcode(stuffp, &stuffp->start, - 1)) - return -EIO; - return 0; - } - - case CDROMMULTISESSION:{ - struct cdrom_multisession *ms = - (struct cdrom_multisession *) arg; - xtrace(IOCTL, "ioctl() MULTISESSION\n"); - /* Always return stuff in LBA, and let the Uniform cdrom driver - worry about what the user actually wants */ - ms->addr.lba = msf2log(&stuffp->multi.msf_last); - ms->xa_flag = !!stuffp->multi.multi; - xtrace(MS, - "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n", - ms->xa_flag, ms->addr.lba, - stuffp->multi.msf_last.minute, - stuffp->multi.msf_last.second, - stuffp->multi.msf_last.frame); - - return 0; - } - - case CDROMEJECT:{ - xtrace(IOCTL, "ioctl() EJECT\n"); - if (stuffp->users > 1) - return -EBUSY; - return (mcdx_tray_move(cdi, 1)); - } - - case CDROMCLOSETRAY:{ - xtrace(IOCTL, "ioctl() CDROMCLOSETRAY\n"); - return (mcdx_tray_move(cdi, 0)); - } - - case CDROMVOLCTRL:{ - struct cdrom_volctrl *volctrl = - (struct cdrom_volctrl *) arg; - xtrace(IOCTL, "ioctl() VOLCTRL\n"); - -#if 0 /* not tested! */ - /* adjust for the weirdness of workman (md) */ - /* can't test it (hs) */ - volctrl.channel2 = volctrl.channel1; - volctrl.channel1 = volctrl.channel3 = 0x00; -#endif - return mcdx_setattentuator(stuffp, volctrl, 2); - } - - default: - return -EINVAL; - } -} - -static void do_mcdx_request(request_queue_t * q) -{ - struct s_drive_stuff *stuffp; - struct request *req; - - again: - - req = elv_next_request(q); - if (!req) - return; - - stuffp = req->rq_disk->private_data; - - if (!stuffp->present) { - xwarn("do_request(): bad device: %s\n",req->rq_disk->disk_name); - xtrace(REQUEST, "end_request(0): bad device\n"); - end_request(req, 0); - return; - } - - if (stuffp->audio) { - xwarn("do_request() attempt to read from audio cd\n"); - xtrace(REQUEST, "end_request(0): read from audio\n"); - end_request(req, 0); - return; - } - - xtrace(REQUEST, "do_request() (%lu + %lu)\n", - req->sector, req->nr_sectors); - - if (req->cmd != READ) { - xwarn("do_request(): non-read command to cd!!\n"); - xtrace(REQUEST, "end_request(0): write\n"); - end_request(req, 0); - return; - } - else { - stuffp->status = 0; - while (req->nr_sectors) { - int i; - - i = mcdx_transfer(stuffp, - req->buffer, - req->sector, - req->nr_sectors); - - if (i == -1) { - end_request(req, 0); - goto again; - } - req->sector += i; - req->nr_sectors -= i; - req->buffer += (i * 512); - } - end_request(req, 1); - goto again; - - xtrace(REQUEST, "end_request(1)\n"); - end_request(req, 1); - } - - goto again; -} - -static int mcdx_open(struct cdrom_device_info *cdi, int purpose) -{ - struct s_drive_stuff *stuffp; - xtrace(OPENCLOSE, "open()\n"); - stuffp = cdi->handle; - if (!stuffp->present) - return -ENXIO; - - /* Make the modules looking used ... (thanx bjorn). - * But we shouldn't forget to decrement the module counter - * on error return */ - - /* this is only done to test if the drive talks with us */ - if (-1 == mcdx_getstatus(stuffp, 1)) - return -EIO; - - if (stuffp->xxx) { - - xtrace(OPENCLOSE, "open() media changed\n"); - stuffp->audiostatus = CDROM_AUDIO_INVALID; - stuffp->readcmd = 0; - xtrace(OPENCLOSE, "open() Request multisession info\n"); - if (-1 == - mcdx_requestmultidiskinfo(stuffp, &stuffp->multi, 6)) - xinfo("No multidiskinfo\n"); - } else { - /* multisession ? */ - if (!stuffp->multi.multi) - stuffp->multi.msf_last.second = 2; - - xtrace(OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n", - stuffp->multi.multi, - stuffp->multi.msf_last.minute, - stuffp->multi.msf_last.second, - stuffp->multi.msf_last.frame); - - {; - } /* got multisession information */ - /* request the disks table of contents (aka diskinfo) */ - if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) { - - stuffp->lastsector = -1; - - } else { - - stuffp->lastsector = (CD_FRAMESIZE / 512) - * msf2log(&stuffp->di.msf_leadout) - 1; - - xtrace(OPENCLOSE, - "open() start %d (%02x:%02x.%02x) %d\n", - stuffp->di.n_first, - stuffp->di.msf_first.minute, - stuffp->di.msf_first.second, - stuffp->di.msf_first.frame, - msf2log(&stuffp->di.msf_first)); - xtrace(OPENCLOSE, - "open() last %d (%02x:%02x.%02x) %d\n", - stuffp->di.n_last, - stuffp->di.msf_leadout.minute, - stuffp->di.msf_leadout.second, - stuffp->di.msf_leadout.frame, - msf2log(&stuffp->di.msf_leadout)); - } - - if (stuffp->toc) { - xtrace(MALLOC, "open() free old toc @ %p\n", - stuffp->toc); - kfree(stuffp->toc); - - stuffp->toc = NULL; - } - - xtrace(OPENCLOSE, "open() init irq generation\n"); - if (-1 == mcdx_config(stuffp, 1)) - return -EIO; -#ifdef FALLBACK - /* Set the read speed */ - xwarn("AAA %x AAA\n", stuffp->readcmd); - if (stuffp->readerrs) - stuffp->readcmd = READ1X; - else - stuffp->readcmd = - stuffp->present | SINGLE ? READ1X : READ2X; - xwarn("XXX %x XXX\n", stuffp->readcmd); -#else - stuffp->readcmd = - stuffp->present | SINGLE ? READ1X : READ2X; -#endif - - /* try to get the first sector, iff any ... */ - if (stuffp->lastsector >= 0) { - char buf[512]; - int ans; - int tries; - - stuffp->xa = 0; - stuffp->audio = 0; - - for (tries = 6; tries; tries--) { - - stuffp->introk = 1; - - xtrace(OPENCLOSE, "open() try as %s\n", - stuffp->xa ? "XA" : "normal"); - /* set data mode */ - if (-1 == (ans = mcdx_setdatamode(stuffp, - stuffp-> - xa ? - MODE2 : - MODE1, - 1))) { - /* return -EIO; */ - stuffp->xa = 0; - break; - } - - if ((stuffp->audio = e_audio(ans))) - break; - - while (0 == - (ans = - mcdx_transfer(stuffp, buf, 0, 1))); - - if (ans == 1) - break; - stuffp->xa = !stuffp->xa; - } - } - /* xa disks will be read in raw mode, others not */ - if (-1 == mcdx_setdrivemode(stuffp, - stuffp->xa ? RAW : COOKED, - 1)) - return -EIO; - if (stuffp->audio) { - xinfo("open() audio disk found\n"); - } else if (stuffp->lastsector >= 0) { - xinfo("open() %s%s disk found\n", - stuffp->xa ? "XA / " : "", - stuffp->multi. - multi ? "Multi Session" : "Single Session"); - } - } - stuffp->xxx = 0; - stuffp->users++; - return 0; -} - -static void mcdx_close(struct cdrom_device_info *cdi) -{ - struct s_drive_stuff *stuffp; - - xtrace(OPENCLOSE, "close()\n"); - - stuffp = cdi->handle; - - --stuffp->users; -} - -static int mcdx_media_changed(struct cdrom_device_info *cdi, int disc_nr) -/* Return: 1 if media changed since last call to this function - 0 otherwise */ -{ - struct s_drive_stuff *stuffp; - - xinfo("mcdx_media_changed called for device %s\n", cdi->name); - - stuffp = cdi->handle; - mcdx_getstatus(stuffp, 1); - - if (stuffp->yyy == 0) - return 0; - - stuffp->yyy = 0; - return 1; -} - -#ifndef MODULE -static int __init mcdx_setup(char *str) -{ - int pi[4]; - (void) get_options(str, ARRAY_SIZE(pi), pi); - - if (pi[0] > 0) - mcdx_drive_map[0][0] = pi[1]; - if (pi[0] > 1) - mcdx_drive_map[0][1] = pi[2]; - return 1; -} - -__setup("mcdx=", mcdx_setup); - -#endif - -/* DIRTY PART ******************************************************/ - -static void mcdx_delay(struct s_drive_stuff *stuff, long jifs) -/* This routine is used for sleeping. - * A jifs value <0 means NO sleeping, - * =0 means minimal sleeping (let the kernel - * run for other processes) - * >0 means at least sleep for that amount. - * May be we could use a simple count loop w/ jumps to itself, but - * I wanna make this independent of cpu speed. [1 jiffy is 1/HZ] sec */ -{ - if (jifs < 0) - return; - - xtrace(SLEEP, "*** delay: sleepq\n"); - interruptible_sleep_on_timeout(&stuff->sleepq, jifs); - xtrace(SLEEP, "delay awoken\n"); - if (signal_pending(current)) { - xtrace(SLEEP, "got signal\n"); - } -} - -static irqreturn_t mcdx_intr(int irq, void *dev_id) -{ - struct s_drive_stuff *stuffp = dev_id; - unsigned char b; - -#ifdef AK2 - if (!stuffp->busy && stuffp->pending) - stuffp->int_err = 1; - -#endif /* AK2 */ - /* get the interrupt status */ - b = inb(stuffp->rreg_status); - stuffp->introk = ~b & MCDX_RBIT_DTEN; - - /* NOTE: We only should get interrupts if the data we - * requested are ready to transfer. - * But the drive seems to generate ``asynchronous'' interrupts - * on several error conditions too. (Despite the err int enable - * setting during initialisation) */ - - /* if not ok, read the next byte as the drives status */ - if (!stuffp->introk) { - xtrace(IRQ, "intr() irq %d hw status 0x%02x\n", irq, b); - if (~b & MCDX_RBIT_STEN) { - xinfo("intr() irq %d status 0x%02x\n", - irq, inb(stuffp->rreg_data)); - } else { - xinfo("intr() irq %d ambiguous hw status\n", irq); - } - } else { - xtrace(IRQ, "irq() irq %d ok, status %02x\n", irq, b); - } - - stuffp->busy = 0; - wake_up_interruptible(&stuffp->busyq); - return IRQ_HANDLED; -} - - -static int mcdx_talk(struct s_drive_stuff *stuffp, - const unsigned char *cmd, size_t cmdlen, - void *buffer, size_t size, unsigned int timeout, int tries) -/* Send a command to the drive, wait for the result. - * returns -1 on timeout, drive status otherwise - * If buffer is not zero, the result (length size) is stored there. - * If buffer is zero the size should be the number of bytes to read - * from the drive. These bytes are discarded. - */ -{ - int st; - char c; - int discard; - - /* Somebody wants the data read? */ - if ((discard = (buffer == NULL))) - buffer = &c; - - while (stuffp->lock) { - xtrace(SLEEP, "*** talk: lockq\n"); - interruptible_sleep_on(&stuffp->lockq); - xtrace(SLEEP, "talk: awoken\n"); - } - - stuffp->lock = 1; - - /* An operation other then reading data destroys the - * data already requested and remembered in stuffp->request, ... */ - stuffp->valid = 0; - -#if MCDX_DEBUG & TALK - { - unsigned char i; - xtrace(TALK, - "talk() %d / %d tries, res.size %d, command 0x%02x", - tries, timeout, size, (unsigned char) cmd[0]); - for (i = 1; i < cmdlen; i++) - xtrace(TALK, " 0x%02x", cmd[i]); - xtrace(TALK, "\n"); - } -#endif - - /* give up if all tries are done (bad) or if the status - * st != -1 (good) */ - for (st = -1; st == -1 && tries; tries--) { - - char *bp = (char *) buffer; - size_t sz = size; - - outsb(stuffp->wreg_data, cmd, cmdlen); - xtrace(TALK, "talk() command sent\n"); - - /* get the status byte */ - if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) { - xinfo("talk() %02x timed out (status), %d tr%s left\n", - cmd[0], tries - 1, tries == 2 ? "y" : "ies"); - continue; - } - st = *bp; - sz--; - if (!discard) - bp++; - - xtrace(TALK, "talk() got status 0x%02x\n", st); - - /* command error? */ - if (e_cmderr(st)) { - xwarn("command error cmd = %02x %s \n", - cmd[0], cmdlen > 1 ? "..." : ""); - st = -1; - continue; - } - - /* audio status? */ - if (stuffp->audiostatus == CDROM_AUDIO_INVALID) - stuffp->audiostatus = - e_audiobusy(st) ? CDROM_AUDIO_PLAY : - CDROM_AUDIO_NO_STATUS; - else if (stuffp->audiostatus == CDROM_AUDIO_PLAY - && e_audiobusy(st) == 0) - stuffp->audiostatus = CDROM_AUDIO_COMPLETED; - - /* media change? */ - if (e_changed(st)) { - xinfo("talk() media changed\n"); - stuffp->xxx = stuffp->yyy = 1; - } - - /* now actually get the data */ - while (sz--) { - if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) { - xinfo("talk() %02x timed out (data), %d tr%s left\n", - cmd[0], tries - 1, - tries == 2 ? "y" : "ies"); - st = -1; - break; - } - if (!discard) - bp++; - xtrace(TALK, "talk() got 0x%02x\n", *(bp - 1)); - } - } - -#if !MCDX_QUIET - if (!tries && st == -1) - xinfo("talk() giving up\n"); -#endif - - stuffp->lock = 0; - wake_up_interruptible(&stuffp->lockq); - - xtrace(TALK, "talk() done with 0x%02x\n", st); - return st; -} - -/* MODULE STUFF ***********************************************************/ - -static int __init __mcdx_init(void) -{ - int i; - int drives = 0; - - mcdx_init(); - for (i = 0; i < MCDX_NDRIVES; i++) { - if (mcdx_stuffp[i]) { - xtrace(INIT, "init_module() drive %d stuff @ %p\n", - i, mcdx_stuffp[i]); - drives++; - } - } - - if (!drives) - return -EIO; - - return 0; -} - -static void __exit mcdx_exit(void) -{ - int i; - - xinfo("cleanup_module called\n"); - - for (i = 0; i < MCDX_NDRIVES; i++) { - struct s_drive_stuff *stuffp = mcdx_stuffp[i]; - if (!stuffp) - continue; - del_gendisk(stuffp->disk); - if (unregister_cdrom(&stuffp->info)) { - printk(KERN_WARNING "Can't unregister cdrom mcdx\n"); - continue; - } - put_disk(stuffp->disk); - release_region(stuffp->wreg_data, MCDX_IO_SIZE); - free_irq(stuffp->irq, NULL); - if (stuffp->toc) { - xtrace(MALLOC, "cleanup_module() free toc @ %p\n", - stuffp->toc); - kfree(stuffp->toc); - } - xtrace(MALLOC, "cleanup_module() free stuffp @ %p\n", - stuffp); - mcdx_stuffp[i] = NULL; - kfree(stuffp); - } - - if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) { - xwarn("cleanup() unregister_blkdev() failed\n"); - } -#if !MCDX_QUIET - else - xinfo("cleanup() succeeded\n"); -#endif - blk_cleanup_queue(mcdx_queue); -} - -#ifdef MODULE -module_init(__mcdx_init); -#endif -module_exit(mcdx_exit); - - -/* Support functions ************************************************/ - -static int __init mcdx_init_drive(int drive) -{ - struct s_version version; - struct gendisk *disk; - struct s_drive_stuff *stuffp; - int size = sizeof(*stuffp); - char msg[80]; - - xtrace(INIT, "init() try drive %d\n", drive); - - xtrace(INIT, "kmalloc space for stuffpt's\n"); - xtrace(MALLOC, "init() malloc %d bytes\n", size); - if (!(stuffp = kzalloc(size, GFP_KERNEL))) { - xwarn("init() malloc failed\n"); - return 1; - } - - disk = alloc_disk(1); - if (!disk) { - xwarn("init() malloc failed\n"); - kfree(stuffp); - return 1; - } - - xtrace(INIT, "init() got %d bytes for drive stuff @ %p\n", - sizeof(*stuffp), stuffp); - - /* set default values */ - stuffp->present = 0; /* this should be 0 already */ - stuffp->toc = NULL; /* this should be NULL already */ - - /* setup our irq and i/o addresses */ - stuffp->irq = irq(mcdx_drive_map[drive]); - stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]); - stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1; - stuffp->wreg_hcon = stuffp->wreg_reset + 1; - stuffp->wreg_chn = stuffp->wreg_hcon + 1; - - init_waitqueue_head(&stuffp->busyq); - init_waitqueue_head(&stuffp->lockq); - init_waitqueue_head(&stuffp->sleepq); - - /* check if i/o addresses are available */ - if (!request_region(stuffp->wreg_data, MCDX_IO_SIZE, "mcdx")) { - xwarn("0x%03x,%d: Init failed. " - "I/O ports (0x%03x..0x%03x) already in use.\n", - stuffp->wreg_data, stuffp->irq, - stuffp->wreg_data, - stuffp->wreg_data + MCDX_IO_SIZE - 1); - xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp); - kfree(stuffp); - put_disk(disk); - xtrace(INIT, "init() continue at next drive\n"); - return 0; /* next drive */ - } - - xtrace(INIT, "init() i/o port is available at 0x%03x\n" - stuffp->wreg_data); - xtrace(INIT, "init() hardware reset\n"); - mcdx_reset(stuffp, HARD, 1); - - xtrace(INIT, "init() get version\n"); - if (-1 == mcdx_requestversion(stuffp, &version, 4)) { - /* failed, next drive */ - release_region(stuffp->wreg_data, MCDX_IO_SIZE); - xwarn("%s=0x%03x,%d: Init failed. Can't get version.\n", - MCDX, stuffp->wreg_data, stuffp->irq); - xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp); - kfree(stuffp); - put_disk(disk); - xtrace(INIT, "init() continue at next drive\n"); - return 0; - } - - switch (version.code) { - case 'D': - stuffp->readcmd = READ2X; - stuffp->present = DOUBLE | DOOR | MULTI; - break; - case 'F': - stuffp->readcmd = READ1X; - stuffp->present = SINGLE | DOOR | MULTI; - break; - case 'M': - stuffp->readcmd = READ1X; - stuffp->present = SINGLE; - break; - default: - stuffp->present = 0; - break; - } - - stuffp->playcmd = READ1X; - - if (!stuffp->present) { - release_region(stuffp->wreg_data, MCDX_IO_SIZE); - xwarn("%s=0x%03x,%d: Init failed. No Mitsumi CD-ROM?.\n", - MCDX, stuffp->wreg_data, stuffp->irq); - kfree(stuffp); - put_disk(disk); - return 0; /* next drive */ - } - - xtrace(INIT, "init() register blkdev\n"); - if (register_blkdev(MAJOR_NR, "mcdx")) { - release_region(stuffp->wreg_data, MCDX_IO_SIZE); - kfree(stuffp); - put_disk(disk); - return 1; - } - - mcdx_queue = blk_init_queue(do_mcdx_request, &mcdx_lock); - if (!mcdx_queue) { - unregister_blkdev(MAJOR_NR, "mcdx"); - release_region(stuffp->wreg_data, MCDX_IO_SIZE); - kfree(stuffp); - put_disk(disk); - return 1; - } - - xtrace(INIT, "init() subscribe irq and i/o\n"); - if (request_irq(stuffp->irq, mcdx_intr, IRQF_DISABLED, "mcdx", stuffp)) { - release_region(stuffp->wreg_data, MCDX_IO_SIZE); - xwarn("%s=0x%03x,%d: Init failed. Can't get irq (%d).\n", - MCDX, stuffp->wreg_data, stuffp->irq, stuffp->irq); - stuffp->irq = 0; - blk_cleanup_queue(mcdx_queue); - kfree(stuffp); - put_disk(disk); - return 0; - } - - xtrace(INIT, "init() get garbage\n"); - { - int i; - mcdx_delay(stuffp, HZ / 2); - for (i = 100; i; i--) - (void) inb(stuffp->rreg_status); - } - - -#ifdef WE_KNOW_WHY - /* irq 11 -> channel register */ - outb(0x50, stuffp->wreg_chn); -#endif - - xtrace(INIT, "init() set non dma but irq mode\n"); - mcdx_config(stuffp, 1); - - stuffp->info.ops = &mcdx_dops; - stuffp->info.speed = 2; - stuffp->info.capacity = 1; - stuffp->info.handle = stuffp; - sprintf(stuffp->info.name, "mcdx%d", drive); - disk->major = MAJOR_NR; - disk->first_minor = drive; - strcpy(disk->disk_name, stuffp->info.name); - disk->fops = &mcdx_bdops; - disk->flags = GENHD_FL_CD; - stuffp->disk = disk; - - sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%03x, irq %d." - " (Firmware version %c %x)\n", - stuffp->wreg_data, stuffp->irq, version.code, version.ver); - mcdx_stuffp[drive] = stuffp; - xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp); - if (register_cdrom(&stuffp->info) != 0) { - printk("Cannot register Mitsumi CD-ROM!\n"); - free_irq(stuffp->irq, NULL); - release_region(stuffp->wreg_data, MCDX_IO_SIZE); - kfree(stuffp); - put_disk(disk); - if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) - xwarn("cleanup() unregister_blkdev() failed\n"); - blk_cleanup_queue(mcdx_queue); - return 2; - } - disk->private_data = stuffp; - disk->queue = mcdx_queue; - add_disk(disk); - printk(msg); - return 0; -} - -static int __init mcdx_init(void) -{ - int drive; - xwarn("Version 2.14(hs) \n"); - - xwarn("$Id: mcdx.c,v 1.21 1997/01/26 07:12:59 davem Exp $\n"); - - /* zero the pointer array */ - for (drive = 0; drive < MCDX_NDRIVES; drive++) - mcdx_stuffp[drive] = NULL; - - /* do the initialisation */ - for (drive = 0; drive < MCDX_NDRIVES; drive++) { - switch (mcdx_init_drive(drive)) { - case 2: - return -EIO; - case 1: - break; - } - } - return 0; -} - -static int mcdx_transfer(struct s_drive_stuff *stuffp, - char *p, int sector, int nr_sectors) -/* This seems to do the actually transfer. But it does more. It - keeps track of errors occurred and will (if possible) fall back - to single speed on error. - Return: -1 on timeout or other error - else status byte (as in stuff->st) */ -{ - int ans; - - ans = mcdx_xfer(stuffp, p, sector, nr_sectors); - return ans; -#ifdef FALLBACK - if (-1 == ans) - stuffp->readerrs++; - else - return ans; - - if (stuffp->readerrs && stuffp->readcmd == READ1X) { - xwarn("XXX Already reading 1x -- no chance\n"); - return -1; - } - - xwarn("XXX Fallback to 1x\n"); - - stuffp->readcmd = READ1X; - return mcdx_transfer(stuffp, p, sector, nr_sectors); -#endif - -} - - -static int mcdx_xfer(struct s_drive_stuff *stuffp, - char *p, int sector, int nr_sectors) -/* This does actually the transfer from the drive. - Return: -1 on timeout or other error - else status byte (as in stuff->st) */ -{ - int border; - int done = 0; - long timeout; - - if (stuffp->audio) { - xwarn("Attempt to read from audio CD.\n"); - return -1; - } - - if (!stuffp->readcmd) { - xinfo("Can't transfer from missing disk.\n"); - return -1; - } - - while (stuffp->lock) { - interruptible_sleep_on(&stuffp->lockq); - } - - if (stuffp->valid && (sector >= stuffp->pending) - && (sector < stuffp->low_border)) { - - /* All (or at least a part of the sectors requested) seems - * to be already requested, so we don't need to bother the - * drive with new requests ... - * Wait for the drive become idle, but first - * check for possible occurred errors --- the drive - * seems to report them asynchronously */ - - - border = stuffp->high_border < (border = - sector + nr_sectors) - ? stuffp->high_border : border; - - stuffp->lock = current->pid; - - do { - - while (stuffp->busy) { - - timeout = - interruptible_sleep_on_timeout - (&stuffp->busyq, 5 * HZ); - - if (!stuffp->introk) { - xtrace(XFER, - "error via interrupt\n"); - } else if (!timeout) { - xtrace(XFER, "timeout\n"); - } else if (signal_pending(current)) { - xtrace(XFER, "signal\n"); - } else - continue; - - stuffp->lock = 0; - stuffp->busy = 0; - stuffp->valid = 0; - - wake_up_interruptible(&stuffp->lockq); - xtrace(XFER, "transfer() done (-1)\n"); - return -1; - } - - /* check if we need to set the busy flag (as we - * expect an interrupt */ - stuffp->busy = (3 == (stuffp->pending & 3)); - - /* Test if it's the first sector of a block, - * there we have to skip some bytes as we read raw data */ - if (stuffp->xa && (0 == (stuffp->pending & 3))) { - const int HEAD = - CD_FRAMESIZE_RAW - CD_XA_TAIL - - CD_FRAMESIZE; - insb(stuffp->rreg_data, p, HEAD); - } - - /* now actually read the data */ - insb(stuffp->rreg_data, p, 512); - - /* test if it's the last sector of a block, - * if so, we have to handle XA special */ - if ((3 == (stuffp->pending & 3)) && stuffp->xa) { - char dummy[CD_XA_TAIL]; - insb(stuffp->rreg_data, &dummy[0], CD_XA_TAIL); - } - - if (stuffp->pending == sector) { - p += 512; - done++; - sector++; - } - } while (++(stuffp->pending) < border); - - stuffp->lock = 0; - wake_up_interruptible(&stuffp->lockq); - - } else { - - /* The requested sector(s) is/are out of the - * already requested range, so we have to bother the drive - * with a new request. */ - - static unsigned char cmd[] = { - 0, - 0, 0, 0, - 0, 0, 0 - }; - - cmd[0] = stuffp->readcmd; - - /* The numbers held in ->pending, ..., should be valid */ - stuffp->valid = 1; - stuffp->pending = sector & ~3; - - /* do some sanity checks */ - if (stuffp->pending > stuffp->lastsector) { - xwarn - ("transfer() sector %d from nirvana requested.\n", - stuffp->pending); - stuffp->status = MCDX_ST_EOM; - stuffp->valid = 0; - xtrace(XFER, "transfer() done (-1)\n"); - return -1; - } - - if ((stuffp->low_border = stuffp->pending + DIRECT_SIZE) - > stuffp->lastsector + 1) { - xtrace(XFER, "cut low_border\n"); - stuffp->low_border = stuffp->lastsector + 1; - } - if ((stuffp->high_border = stuffp->pending + REQUEST_SIZE) - > stuffp->lastsector + 1) { - xtrace(XFER, "cut high_border\n"); - stuffp->high_border = stuffp->lastsector + 1; - } - - { /* Convert the sector to be requested to MSF format */ - struct cdrom_msf0 pending; - log2msf(stuffp->pending / 4, &pending); - cmd[1] = pending.minute; - cmd[2] = pending.second; - cmd[3] = pending.frame; - } - - cmd[6] = - (unsigned - char) ((stuffp->high_border - stuffp->pending) / 4); - xtrace(XFER, "[%2d]\n", cmd[6]); - - stuffp->busy = 1; - /* Now really issue the request command */ - outsb(stuffp->wreg_data, cmd, sizeof cmd); - - } -#ifdef AK2 - if (stuffp->int_err) { - stuffp->valid = 0; - stuffp->int_err = 0; - return -1; - } -#endif /* AK2 */ - - stuffp->low_border = (stuffp->low_border += - done) < - stuffp->high_border ? stuffp->low_border : stuffp->high_border; - - return done; -} - - -/* Access to elements of the mcdx_drive_map members */ - -static unsigned port(int *ip) -{ - return ip[0]; -} -static int irq(int *ip) -{ - return ip[1]; -} - -/* Misc number converters */ - -static unsigned int bcd2uint(unsigned char c) -{ - return (c >> 4) * 10 + (c & 0x0f); -} - -static unsigned int uint2bcd(unsigned int ival) -{ - return ((ival / 10) << 4) | (ival % 10); -} - -static void log2msf(unsigned int l, struct cdrom_msf0 *pmsf) -{ - l += CD_MSF_OFFSET; - pmsf->minute = uint2bcd(l / 4500), l %= 4500; - pmsf->second = uint2bcd(l / 75); - pmsf->frame = uint2bcd(l % 75); -} - -static unsigned int msf2log(const struct cdrom_msf0 *pmsf) -{ - return bcd2uint(pmsf->frame) - + bcd2uint(pmsf->second) * 75 - + bcd2uint(pmsf->minute) * 4500 - CD_MSF_OFFSET; -} - -int mcdx_readtoc(struct s_drive_stuff *stuffp) -/* Read the toc entries from the CD, - * Return: -1 on failure, else 0 */ -{ - - if (stuffp->toc) { - xtrace(READTOC, "ioctl() toc already read\n"); - return 0; - } - - xtrace(READTOC, "ioctl() readtoc for %d tracks\n", - stuffp->di.n_last - stuffp->di.n_first + 1); - - if (-1 == mcdx_hold(stuffp, 1)) - return -1; - - xtrace(READTOC, "ioctl() tocmode\n"); - if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) - return -EIO; - - /* all seems to be ok so far ... malloc */ - { - int size; - size = - sizeof(struct s_subqcode) * (stuffp->di.n_last - - stuffp->di.n_first + 2); - - xtrace(MALLOC, "ioctl() malloc %d bytes\n", size); - stuffp->toc = kmalloc(size, GFP_KERNEL); - if (!stuffp->toc) { - xwarn("Cannot malloc %d bytes for toc\n", size); - mcdx_setdrivemode(stuffp, DATA, 1); - return -EIO; - } - } - - /* now read actually the index */ - { - int trk; - int retries; - - for (trk = 0; - trk < (stuffp->di.n_last - stuffp->di.n_first + 1); - trk++) - stuffp->toc[trk].index = 0; - - for (retries = 300; retries; retries--) { /* why 300? */ - struct s_subqcode q; - unsigned int idx; - - if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) { - mcdx_setdrivemode(stuffp, DATA, 1); - return -EIO; - } - - idx = bcd2uint(q.index); - - if ((idx > 0) - && (idx <= stuffp->di.n_last) - && (q.tno == 0) - && (stuffp->toc[idx - stuffp->di.n_first]. - index == 0)) { - stuffp->toc[idx - stuffp->di.n_first] = q; - xtrace(READTOC, - "ioctl() toc idx %d (trk %d)\n", - idx, trk); - trk--; - } - if (trk == 0) - break; - } - memset(&stuffp-> - toc[stuffp->di.n_last - stuffp->di.n_first + 1], 0, - sizeof(stuffp->toc[0])); - stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + - 1].dt = stuffp->di.msf_leadout; - } - - /* unset toc mode */ - xtrace(READTOC, "ioctl() undo toc mode\n"); - if (-1 == mcdx_setdrivemode(stuffp, DATA, 2)) - return -EIO; - -#if MCDX_DEBUG && READTOC - { - int trk; - for (trk = 0; - trk < (stuffp->di.n_last - stuffp->di.n_first + 2); - trk++) - xtrace(READTOC, "ioctl() %d readtoc %02x %02x %02x" - " %02x:%02x.%02x %02x:%02x.%02x\n", - trk + stuffp->di.n_first, - stuffp->toc[trk].control, - stuffp->toc[trk].tno, - stuffp->toc[trk].index, - stuffp->toc[trk].tt.minute, - stuffp->toc[trk].tt.second, - stuffp->toc[trk].tt.frame, - stuffp->toc[trk].dt.minute, - stuffp->toc[trk].dt.second, - stuffp->toc[trk].dt.frame); - } -#endif - - return 0; -} - -static int -mcdx_playmsf(struct s_drive_stuff *stuffp, const struct cdrom_msf *msf) -{ - unsigned char cmd[7] = { - 0, 0, 0, 0, 0, 0, 0 - }; - - if (!stuffp->readcmd) { - xinfo("Can't play from missing disk.\n"); - return -1; - } - - cmd[0] = stuffp->playcmd; - - cmd[1] = msf->cdmsf_min0; - cmd[2] = msf->cdmsf_sec0; - cmd[3] = msf->cdmsf_frame0; - cmd[4] = msf->cdmsf_min1; - cmd[5] = msf->cdmsf_sec1; - cmd[6] = msf->cdmsf_frame1; - - xtrace(PLAYMSF, "ioctl(): play %x " - "%02x:%02x:%02x -- %02x:%02x:%02x\n", - cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]); - - outsb(stuffp->wreg_data, cmd, sizeof cmd); - - if (-1 == mcdx_getval(stuffp, 3 * HZ, 0, NULL)) { - xwarn("playmsf() timeout\n"); - return -1; - } - - stuffp->audiostatus = CDROM_AUDIO_PLAY; - return 0; -} - -static int -mcdx_playtrk(struct s_drive_stuff *stuffp, const struct cdrom_ti *ti) -{ - struct s_subqcode *p; - struct cdrom_msf msf; - - if (-1 == mcdx_readtoc(stuffp)) - return -1; - - if (ti) - p = &stuffp->toc[ti->cdti_trk0 - stuffp->di.n_first]; - else - p = &stuffp->start; - - msf.cdmsf_min0 = p->dt.minute; - msf.cdmsf_sec0 = p->dt.second; - msf.cdmsf_frame0 = p->dt.frame; - - if (ti) { - p = &stuffp->toc[ti->cdti_trk1 - stuffp->di.n_first + 1]; - stuffp->stop = *p; - } else - p = &stuffp->stop; - - msf.cdmsf_min1 = p->dt.minute; - msf.cdmsf_sec1 = p->dt.second; - msf.cdmsf_frame1 = p->dt.frame; - - return mcdx_playmsf(stuffp, &msf); -} - - -/* Drive functions ************************************************/ - -static int mcdx_tray_move(struct cdrom_device_info *cdi, int position) -{ - struct s_drive_stuff *stuffp = cdi->handle; - - if (!stuffp->present) - return -ENXIO; - if (!(stuffp->present & DOOR)) - return -ENOSYS; - - if (position) /* 1: eject */ - return mcdx_talk(stuffp, "\xf6", 1, NULL, 1, 5 * HZ, 3); - else /* 0: close */ - return mcdx_talk(stuffp, "\xf8", 1, NULL, 1, 5 * HZ, 3); - return 1; -} - -static int mcdx_stop(struct s_drive_stuff *stuffp, int tries) -{ - return mcdx_talk(stuffp, "\xf0", 1, NULL, 1, 2 * HZ, tries); -} - -static int mcdx_hold(struct s_drive_stuff *stuffp, int tries) -{ - return mcdx_talk(stuffp, "\x70", 1, NULL, 1, 2 * HZ, tries); -} - -static int mcdx_requestsubqcode(struct s_drive_stuff *stuffp, - struct s_subqcode *sub, int tries) -{ - char buf[11]; - int ans; - - if (-1 == (ans = mcdx_talk(stuffp, "\x20", 1, buf, sizeof(buf), - 2 * HZ, tries))) - return -1; - sub->control = buf[1]; - sub->tno = buf[2]; - sub->index = buf[3]; - sub->tt.minute = buf[4]; - sub->tt.second = buf[5]; - sub->tt.frame = buf[6]; - sub->dt.minute = buf[8]; - sub->dt.second = buf[9]; - sub->dt.frame = buf[10]; - - return ans; -} - -static int mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp, - struct s_multi *multi, int tries) -{ - char buf[5]; - int ans; - - if (stuffp->present & MULTI) { - ans = - mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 2 * HZ, - tries); - multi->multi = buf[1]; - multi->msf_last.minute = buf[2]; - multi->msf_last.second = buf[3]; - multi->msf_last.frame = buf[4]; - return ans; - } else { - multi->multi = 0; - return 0; - } -} - -static int mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info, - int tries) -{ - char buf[9]; - int ans; - ans = - mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 2 * HZ, tries); - if (ans == -1) { - info->n_first = 0; - info->n_last = 0; - } else { - info->n_first = bcd2uint(buf[1]); - info->n_last = bcd2uint(buf[2]); - info->msf_leadout.minute = buf[3]; - info->msf_leadout.second = buf[4]; - info->msf_leadout.frame = buf[5]; - info->msf_first.minute = buf[6]; - info->msf_first.second = buf[7]; - info->msf_first.frame = buf[8]; - } - return ans; -} - -static int mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, - int tries) -{ - char cmd[2]; - int ans; - - xtrace(HW, "setdrivemode() %d\n", mode); - - if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5 * HZ, tries))) - return -1; - - switch (mode) { - case TOC: - cmd[1] |= 0x04; - break; - case DATA: - cmd[1] &= ~0x04; - break; - case RAW: - cmd[1] |= 0x40; - break; - case COOKED: - cmd[1] &= ~0x40; - break; - default: - break; - } - cmd[0] = 0x50; - return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries); -} - -static int mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, - int tries) -{ - unsigned char cmd[2] = { 0xa0 }; - xtrace(HW, "setdatamode() %d\n", mode); - switch (mode) { - case MODE0: - cmd[1] = 0x00; - break; - case MODE1: - cmd[1] = 0x01; - break; - case MODE2: - cmd[1] = 0x02; - break; - default: - return -EINVAL; - } - return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries); -} - -static int mcdx_config(struct s_drive_stuff *stuffp, int tries) -{ - char cmd[4]; - - xtrace(HW, "config()\n"); - - cmd[0] = 0x90; - - cmd[1] = 0x10; /* irq enable */ - cmd[2] = 0x05; /* pre, err irq enable */ - - if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries)) - return -1; - - cmd[1] = 0x02; /* dma select */ - cmd[2] = 0x00; /* no dma */ - - return mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries); -} - -static int mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver, - int tries) -{ - char buf[3]; - int ans; - - if (-1 == (ans = mcdx_talk(stuffp, "\xdc", - 1, buf, sizeof(buf), 2 * HZ, tries))) - return ans; - - ver->code = buf[1]; - ver->ver = buf[2]; - - return ans; -} - -static int mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries) -{ - if (mode == HARD) { - outb(0, stuffp->wreg_chn); /* no dma, no irq -> hardware */ - outb(0, stuffp->wreg_reset); /* hw reset */ - return 0; - } else - return mcdx_talk(stuffp, "\x60", 1, NULL, 1, 5 * HZ, tries); -} - -static int mcdx_lockdoor(struct cdrom_device_info *cdi, int lock) -{ - struct s_drive_stuff *stuffp = cdi->handle; - char cmd[2] = { 0xfe }; - - if (!(stuffp->present & DOOR)) - return -ENOSYS; - if (stuffp->present & DOOR) { - cmd[1] = lock ? 0x01 : 0x00; - return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 1, 5 * HZ, 3); - } else - return 0; -} - -static int mcdx_getstatus(struct s_drive_stuff *stuffp, int tries) -{ - return mcdx_talk(stuffp, "\x40", 1, NULL, 1, 5 * HZ, tries); -} - -static int -mcdx_getval(struct s_drive_stuff *stuffp, int to, int delay, char *buf) -{ - unsigned long timeout = to + jiffies; - char c; - - if (!buf) - buf = &c; - - while (inb(stuffp->rreg_status) & MCDX_RBIT_STEN) { - if (time_after(jiffies, timeout)) - return -1; - mcdx_delay(stuffp, delay); - } - - *buf = (unsigned char) inb(stuffp->rreg_data) & 0xff; - - return 0; -} - -static int mcdx_setattentuator(struct s_drive_stuff *stuffp, - struct cdrom_volctrl *vol, int tries) -{ - char cmd[5]; - cmd[0] = 0xae; - cmd[1] = vol->channel0; - cmd[2] = 0; - cmd[3] = vol->channel1; - cmd[4] = 0; - - return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 5, 200, tries); -} - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_BLOCKDEV_MAJOR(MITSUMI_X_CDROM_MAJOR); diff --git a/drivers/cdrom/mcdx.h b/drivers/cdrom/mcdx.h deleted file mode 100644 index 83c364a74dc..00000000000 --- a/drivers/cdrom/mcdx.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Definitions for the Mitsumi CDROM interface - * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de> - * VERSION: @VERSION@ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Thanks to - * The Linux Community at all and ... - * Martin Harris (he wrote the first Mitsumi Driver) - * Eberhard Moenkeberg (he gave me much support and the initial kick) - * Bernd Huebner, Ruediger Helsch (Unifix-Software Gmbh, they - * improved the original driver) - * Jon Tombs, Bjorn Ekwall (module support) - * Daniel v. Mosnenck (he sent me the Technical and Programming Reference) - * Gerd Knorr (he lent me his PhotoCD) - * Nils Faerber and Roger E. Wolff (extensively tested the LU portion) - * Andreas Kies (testing the mysterious hang up's) - * ... somebody forgotten? - * Marcin Dalecki - * - */ - -/* - * The following lines are for user configuration - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * {0|1} -- 1 if you want the driver detect your drive, may crash and - * needs a long time to seek. The higher the address the longer the - * seek. - * - * WARNING: AUTOPROBE doesn't work. - */ -#define MCDX_AUTOPROBE 0 - -/* - * Drive specific settings according to the jumpers on the controller - * board(s). - * o MCDX_NDRIVES : number of used entries of the following table - * o MCDX_DRIVEMAP : table of {i/o base, irq} per controller - * - * NOTE: I didn't get a drive at irq 9(2) working. Not even alone. - */ -#if MCDX_AUTOPROBE == 0 - #define MCDX_NDRIVES 1 - #define MCDX_DRIVEMAP { \ - {0x300, 11}, \ - {0x304, 05}, \ - {0x000, 00}, \ - {0x000, 00}, \ - {0x000, 00}, \ - } -#else - #error Autoprobing is not implemented yet. -#endif - -#ifndef MCDX_QUIET -#define MCDX_QUIET 1 -#endif - -#ifndef MCDX_DEBUG -#define MCDX_DEBUG 0 -#endif - -/* *** make the following line uncommented, if you're sure, - * *** all configuration is done */ -/* #define I_WAS_HERE */ - -/* The name of the device */ -#define MCDX "mcdx" - -/* Flags for DEBUGGING */ -#define INIT 0 -#define MALLOC 0 -#define IOCTL 0 -#define PLAYTRK 0 -#define SUBCHNL 0 -#define TOCHDR 0 -#define MS 0 -#define PLAYMSF 0 -#define READTOC 0 -#define OPENCLOSE 0 -#define HW 0 -#define TALK 0 -#define IRQ 0 -#define XFER 0 -#define REQUEST 0 -#define SLEEP 0 - -/* The following addresses are taken from the Mitsumi Reference - * and describe the possible i/o range for the controller. - */ -#define MCDX_IO_BEGIN ((char*) 0x300) /* first base of i/o addr */ -#define MCDX_IO_END ((char*) 0x3fc) /* last base of i/o addr */ - -/* Per controller 4 bytes i/o are needed. */ -#define MCDX_IO_SIZE 4 - -/* - * Bits - */ - -/* The status byte, returned from every command, set if - * the description is true */ -#define MCDX_RBIT_OPEN 0x80 /* door is open */ -#define MCDX_RBIT_DISKSET 0x40 /* disk set (recognised) */ -#define MCDX_RBIT_CHANGED 0x20 /* disk was changed */ -#define MCDX_RBIT_CHECK 0x10 /* disk rotates, servo is on */ -#define MCDX_RBIT_AUDIOTR 0x08 /* current track is audio */ -#define MCDX_RBIT_RDERR 0x04 /* read error, refer SENSE KEY */ -#define MCDX_RBIT_AUDIOBS 0x02 /* currently playing audio */ -#define MCDX_RBIT_CMDERR 0x01 /* command, param or format error */ - -/* The I/O Register holding the h/w status of the drive, - * can be read at i/o base + 1 */ -#define MCDX_RBIT_DOOR 0x10 /* door is open */ -#define MCDX_RBIT_STEN 0x04 /* if 0, i/o base contains drive status */ -#define MCDX_RBIT_DTEN 0x02 /* if 0, i/o base contains data */ - -/* - * The commands. - */ - -#define OPCODE 1 /* offset of opcode */ -#define MCDX_CMD_REQUEST_TOC 1, 0x10 -#define MCDX_CMD_REQUEST_STATUS 1, 0x40 -#define MCDX_CMD_RESET 1, 0x60 -#define MCDX_CMD_REQUEST_DRIVE_MODE 1, 0xc2 -#define MCDX_CMD_SET_INTERLEAVE 2, 0xc8, 0 -#define MCDX_CMD_DATAMODE_SET 2, 0xa0, 0 - #define MCDX_DATAMODE1 0x01 - #define MCDX_DATAMODE2 0x02 -#define MCDX_CMD_LOCK_DOOR 2, 0xfe, 0 - -#define READ_AHEAD 4 /* 8 Sectors (4K) */ - -/* Useful macros */ -#define e_door(x) ((x) & MCDX_RBIT_OPEN) -#define e_check(x) (~(x) & MCDX_RBIT_CHECK) -#define e_notset(x) (~(x) & MCDX_RBIT_DISKSET) -#define e_changed(x) ((x) & MCDX_RBIT_CHANGED) -#define e_audio(x) ((x) & MCDX_RBIT_AUDIOTR) -#define e_audiobusy(x) ((x) & MCDX_RBIT_AUDIOBS) -#define e_cmderr(x) ((x) & MCDX_RBIT_CMDERR) -#define e_readerr(x) ((x) & MCDX_RBIT_RDERR) - -/** no drive specific */ -#define MCDX_CDBLK 2048 /* 2048 cooked data each blk */ - -#define MCDX_DATA_TIMEOUT (HZ/10) /* 0.1 second */ - -/* - * Access to the msf array - */ -#define MSF_MIN 0 /* minute */ -#define MSF_SEC 1 /* second */ -#define MSF_FRM 2 /* frame */ - -/* - * Errors - */ -#define MCDX_E 1 /* unspec error */ -#define MCDX_ST_EOM 0x0100 /* end of media */ -#define MCDX_ST_DRV 0x00ff /* mask to query the drive status */ - -#ifndef I_WAS_HERE -#ifndef MODULE -#warning You have not edited mcdx.h -#warning Perhaps irq and i/o settings are wrong. -#endif -#endif - -/* ex:set ts=4 sw=4: */ diff --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c deleted file mode 100644 index 3541690a77d..00000000000 --- a/drivers/cdrom/optcd.c +++ /dev/null @@ -1,2105 +0,0 @@ -/* linux/drivers/cdrom/optcd.c - Optics Storage 8000 AT CDROM driver - $Id: optcd.c,v 1.11 1997/01/26 07:13:00 davem Exp $ - - Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl) - - - Based on Aztech CD268 CDROM driver by Werner Zimmermann and preworks - by Eberhard Moenkeberg (emoenke@gwdg.de). - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - 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. -*/ - -/* Revision history - - - 14-5-95 v0.0 Plays sound tracks. No reading of data CDs yet. - Detection of disk change doesn't work. - 21-5-95 v0.1 First ALPHA version. CD can be mounted. The - device major nr is borrowed from the Aztech - driver. Speed is around 240 kb/s, as measured - with "time dd if=/dev/cdrom of=/dev/null \ - bs=2048 count=4096". - 24-6-95 v0.2 Reworked the #defines for the command codes - and the like, as well as the structure of - the hardware communication protocol, to - reflect the "official" documentation, kindly - supplied by C.K. Tan, Optics Storage Pte. Ltd. - Also tidied up the state machine somewhat. - 28-6-95 v0.3 Removed the ISP-16 interface code, as this - should go into its own driver. The driver now - has its own major nr. - Disk change detection now seems to work, too. - This version became part of the standard - kernel as of version 1.3.7 - 24-9-95 v0.4 Re-inserted ISP-16 interface code which I - copied from sjcd.c, with a few changes. - Updated README.optcd. Submitted for - inclusion in 1.3.21 - 29-9-95 v0.4a Fixed bug that prevented compilation as module - 25-10-95 v0.5 Started multisession code. Implementation - copied from Werner Zimmermann, who copied it - from Heiko Schlittermann's mcdx. - 17-1-96 v0.6 Multisession works; some cleanup too. - 18-4-96 v0.7 Increased some timing constants; - thanks to Luke McFarlane. Also tidied up some - printk behaviour. ISP16 initialization - is now handled by a separate driver. - - 09-11-99 Make kernel-parameter implementation work with 2.3.x - Removed init_module & cleanup_module in favor of - module_init & module_exit. - Torben Mathiasen <tmm@image.dk> -*/ - -/* Includes */ - - -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/ioport.h> -#include <linux/init.h> - -#include <asm/io.h> -#include <linux/blkdev.h> - -#include <linux/cdrom.h> -#include "optcd.h" - -#include <asm/uaccess.h> - -#define MAJOR_NR OPTICS_CDROM_MAJOR -#define QUEUE (opt_queue) -#define CURRENT elv_next_request(opt_queue) - - -/* Debug support */ - - -/* Don't forget to add new debug flags here. */ -#if DEBUG_DRIVE_IF | DEBUG_VFS | DEBUG_CONV | DEBUG_TOC | \ - DEBUG_BUFFERS | DEBUG_REQUEST | DEBUG_STATE | DEBUG_MULTIS -#define DEBUG(x) debug x -static void debug(int debug_this, const char* fmt, ...) -{ - char s[1024]; - va_list args; - - if (!debug_this) - return; - - va_start(args, fmt); - vsnprintf(s, sizeof(s), fmt, args); - printk(KERN_DEBUG "optcd: %s\n", s); - va_end(args); -} -#else -#define DEBUG(x) -#endif - - -/* Drive hardware/firmware characteristics - Identifiers in accordance with Optics Storage documentation */ - - -#define optcd_port optcd /* Needed for the modutils. */ -static short optcd_port = OPTCD_PORTBASE; /* I/O base of drive. */ -module_param(optcd_port, short, 0); -/* Drive registers, read */ -#define DATA_PORT optcd_port /* Read data/status */ -#define STATUS_PORT optcd_port+1 /* Indicate data/status availability */ - -/* Drive registers, write */ -#define COMIN_PORT optcd_port /* For passing command/parameter */ -#define RESET_PORT optcd_port+1 /* Write anything and wait 0.5 sec */ -#define HCON_PORT optcd_port+2 /* Host Xfer Configuration */ - - -/* Command completion/status read from DATA register */ -#define ST_DRVERR 0x80 -#define ST_DOOR_OPEN 0x40 -#define ST_MIXEDMODE_DISK 0x20 -#define ST_MODE_BITS 0x1c -#define ST_M_STOP 0x00 -#define ST_M_READ 0x04 -#define ST_M_AUDIO 0x04 -#define ST_M_PAUSE 0x08 -#define ST_M_INITIAL 0x0c -#define ST_M_ERROR 0x10 -#define ST_M_OTHERS 0x14 -#define ST_MODE2TRACK 0x02 -#define ST_DSK_CHG 0x01 -#define ST_L_LOCK 0x01 -#define ST_CMD_OK 0x00 -#define ST_OP_OK 0x01 -#define ST_PA_OK 0x02 -#define ST_OP_ERROR 0x05 -#define ST_PA_ERROR 0x06 - - -/* Error codes (appear as command completion code from DATA register) */ -/* Player related errors */ -#define ERR_ILLCMD 0x11 /* Illegal command to player module */ -#define ERR_ILLPARM 0x12 /* Illegal parameter to player module */ -#define ERR_SLEDGE 0x13 -#define ERR_FOCUS 0x14 -#define ERR_MOTOR 0x15 -#define ERR_RADIAL 0x16 -#define ERR_PLL 0x17 /* PLL lock error */ -#define ERR_SUB_TIM 0x18 /* Subcode timeout error */ -#define ERR_SUB_NF 0x19 /* Subcode not found error */ -#define ERR_TRAY 0x1a -#define ERR_TOC 0x1b /* Table of Contents read error */ -#define ERR_JUMP 0x1c -/* Data errors */ -#define ERR_MODE 0x21 -#define ERR_FORM 0x22 -#define ERR_HEADADDR 0x23 /* Header Address not found */ -#define ERR_CRC 0x24 -#define ERR_ECC 0x25 /* Uncorrectable ECC error */ -#define ERR_CRC_UNC 0x26 /* CRC error and uncorrectable error */ -#define ERR_ILLBSYNC 0x27 /* Illegal block sync error */ -#define ERR_VDST 0x28 /* VDST not found */ -/* Timeout errors */ -#define ERR_READ_TIM 0x31 /* Read timeout error */ -#define ERR_DEC_STP 0x32 /* Decoder stopped */ -#define ERR_DEC_TIM 0x33 /* Decoder interrupt timeout error */ -/* Function abort codes */ -#define ERR_KEY 0x41 /* Key -Detected abort */ -#define ERR_READ_FINISH 0x42 /* Read Finish */ -/* Second Byte diagnostic codes */ -#define ERR_NOBSYNC 0x01 /* No block sync */ -#define ERR_SHORTB 0x02 /* Short block */ -#define ERR_LONGB 0x03 /* Long block */ -#define ERR_SHORTDSP 0x04 /* Short DSP word */ -#define ERR_LONGDSP 0x05 /* Long DSP word */ - - -/* Status availability flags read from STATUS register */ -#define FL_EJECT 0x20 -#define FL_WAIT 0x10 /* active low */ -#define FL_EOP 0x08 /* active low */ -#define FL_STEN 0x04 /* Status available when low */ -#define FL_DTEN 0x02 /* Data available when low */ -#define FL_DRQ 0x01 /* active low */ -#define FL_RESET 0xde /* These bits are high after a reset */ -#define FL_STDT (FL_STEN|FL_DTEN) - - -/* Transfer mode, written to HCON register */ -#define HCON_DTS 0x08 -#define HCON_SDRQB 0x04 -#define HCON_LOHI 0x02 -#define HCON_DMA16 0x01 - - -/* Drive command set, written to COMIN register */ -/* Quick response commands */ -#define COMDRVST 0x20 /* Drive Status Read */ -#define COMERRST 0x21 /* Error Status Read */ -#define COMIOCTLISTAT 0x22 /* Status Read; reset disk changed bit */ -#define COMINITSINGLE 0x28 /* Initialize Single Speed */ -#define COMINITDOUBLE 0x29 /* Initialize Double Speed */ -#define COMUNLOCK 0x30 /* Unlock */ -#define COMLOCK 0x31 /* Lock */ -#define COMLOCKST 0x32 /* Lock/Unlock Status */ -#define COMVERSION 0x40 /* Get Firmware Revision */ -#define COMVOIDREADMODE 0x50 /* Void Data Read Mode */ -/* Read commands */ -#define COMFETCH 0x60 /* Prefetch Data */ -#define COMREAD 0x61 /* Read */ -#define COMREADRAW 0x62 /* Read Raw Data */ -#define COMREADALL 0x63 /* Read All 2646 Bytes */ -/* Player control commands */ -#define COMLEADIN 0x70 /* Seek To Lead-in */ -#define COMSEEK 0x71 /* Seek */ -#define COMPAUSEON 0x80 /* Pause On */ -#define COMPAUSEOFF 0x81 /* Pause Off */ -#define COMSTOP 0x82 /* Stop */ -#define COMOPEN 0x90 /* Open Tray Door */ -#define COMCLOSE 0x91 /* Close Tray Door */ -#define COMPLAY 0xa0 /* Audio Play */ -#define COMPLAY_TNO 0xa2 /* Audio Play By Track Number */ -#define COMSUBQ 0xb0 /* Read Sub-q Code */ -#define COMLOCATION 0xb1 /* Read Head Position */ -/* Audio control commands */ -#define COMCHCTRL 0xc0 /* Audio Channel Control */ -/* Miscellaneous (test) commands */ -#define COMDRVTEST 0xd0 /* Write Test Bytes */ -#define COMTEST 0xd1 /* Diagnostic Test */ - -/* Low level drive interface. Only here we do actual I/O - Waiting for status / data available */ - - -/* Busy wait until FLAG goes low. Return 0 on timeout. */ -static inline int flag_low(int flag, unsigned long timeout) -{ - int flag_high; - unsigned long count = 0; - - while ((flag_high = (inb(STATUS_PORT) & flag))) - if (++count >= timeout) - break; - - DEBUG((DEBUG_DRIVE_IF, "flag_low 0x%x count %ld%s", - flag, count, flag_high ? " timeout" : "")); - return !flag_high; -} - - -/* Timed waiting for status or data */ -static int sleep_timeout; /* max # of ticks to sleep */ -static DECLARE_WAIT_QUEUE_HEAD(waitq); -static void sleep_timer(unsigned long data); -static DEFINE_TIMER(delay_timer, sleep_timer, 0, 0); -static DEFINE_SPINLOCK(optcd_lock); -static struct request_queue *opt_queue; - -/* Timer routine: wake up when desired flag goes low, - or when timeout expires. */ -static void sleep_timer(unsigned long data) -{ - int flags = inb(STATUS_PORT) & FL_STDT; - - if (flags == FL_STDT && --sleep_timeout > 0) { - mod_timer(&delay_timer, jiffies + HZ/100); /* multi-statement macro */ - } else - wake_up(&waitq); -} - - -/* Sleep until FLAG goes low. Return 0 on timeout or wrong flag low. */ -static int sleep_flag_low(int flag, unsigned long timeout) -{ - int flag_high; - - DEBUG((DEBUG_DRIVE_IF, "sleep_flag_low")); - - sleep_timeout = timeout; - flag_high = inb(STATUS_PORT) & flag; - if (flag_high && sleep_timeout > 0) { - mod_timer(&delay_timer, jiffies + HZ/100); - sleep_on(&waitq); - flag_high = inb(STATUS_PORT) & flag; - } - - DEBUG((DEBUG_DRIVE_IF, "flag 0x%x count %ld%s", - flag, timeout, flag_high ? " timeout" : "")); - return !flag_high; -} - -/* Low level drive interface. Only here we do actual I/O - Sending commands and parameters */ - - -/* Errors in the command protocol */ -#define ERR_IF_CMD_TIMEOUT 0x100 -#define ERR_IF_ERR_TIMEOUT 0x101 -#define ERR_IF_RESP_TIMEOUT 0x102 -#define ERR_IF_DATA_TIMEOUT 0x103 -#define ERR_IF_NOSTAT 0x104 - - -/* Send command code. Return <0 indicates error */ -static int send_cmd(int cmd) -{ - unsigned char ack; - - DEBUG((DEBUG_DRIVE_IF, "sending command 0x%02x\n", cmd)); - - outb(HCON_DTS, HCON_PORT); /* Enable Suspend Data Transfer */ - outb(cmd, COMIN_PORT); /* Send command code */ - if (!flag_low(FL_STEN, BUSY_TIMEOUT)) /* Wait for status */ - return -ERR_IF_CMD_TIMEOUT; - ack = inb(DATA_PORT); /* read command acknowledge */ - outb(HCON_SDRQB, HCON_PORT); /* Disable Suspend Data Transfer */ - return ack==ST_OP_OK ? 0 : -ack; -} - - -/* Send command parameters. Return <0 indicates error */ -static int send_params(struct cdrom_msf *params) -{ - unsigned char ack; - - DEBUG((DEBUG_DRIVE_IF, "sending parameters" - " %02x:%02x:%02x" - " %02x:%02x:%02x", - params->cdmsf_min0, - params->cdmsf_sec0, - params->cdmsf_frame0, - params->cdmsf_min1, - params->cdmsf_sec1, - params->cdmsf_frame1)); - - outb(params->cdmsf_min0, COMIN_PORT); - outb(params->cdmsf_sec0, COMIN_PORT); - outb(params->cdmsf_frame0, COMIN_PORT); - outb(params->cdmsf_min1, COMIN_PORT); - outb(params->cdmsf_sec1, COMIN_PORT); - outb(params->cdmsf_frame1, COMIN_PORT); - if (!flag_low(FL_STEN, BUSY_TIMEOUT)) /* Wait for status */ - return -ERR_IF_CMD_TIMEOUT; - ack = inb(DATA_PORT); /* read command acknowledge */ - return ack==ST_PA_OK ? 0 : -ack; -} - - -/* Send parameters for SEEK command. Return <0 indicates error */ -static int send_seek_params(struct cdrom_msf *params) -{ - unsigned char ack; - - DEBUG((DEBUG_DRIVE_IF, "sending seek parameters" - " %02x:%02x:%02x", - params->cdmsf_min0, - params->cdmsf_sec0, - params->cdmsf_frame0)); - - outb(params->cdmsf_min0, COMIN_PORT); - outb(params->cdmsf_sec0, COMIN_PORT); - outb(params->cdmsf_frame0, COMIN_PORT); - if (!flag_low(FL_STEN, BUSY_TIMEOUT)) /* Wait for status */ - return -ERR_IF_CMD_TIMEOUT; - ack = inb(DATA_PORT); /* read command acknowledge */ - return ack==ST_PA_OK ? 0 : -ack; -} - - -/* Wait for command execution status. Choice between busy waiting - and sleeping. Return value <0 indicates timeout. */ -static inline int get_exec_status(int busy_waiting) -{ - unsigned char exec_status; - - if (busy_waiting - ? !flag_low(FL_STEN, BUSY_TIMEOUT) - : !sleep_flag_low(FL_STEN, SLEEP_TIMEOUT)) - return -ERR_IF_CMD_TIMEOUT; - - exec_status = inb(DATA_PORT); - DEBUG((DEBUG_DRIVE_IF, "returned exec status 0x%02x", exec_status)); - return exec_status; -} - - -/* Wait busy for extra byte of data that a command returns. - Return value <0 indicates timeout. */ -static inline int get_data(int short_timeout) -{ - unsigned char data; - - if (!flag_low(FL_STEN, short_timeout ? FAST_TIMEOUT : BUSY_TIMEOUT)) - return -ERR_IF_DATA_TIMEOUT; - - data = inb(DATA_PORT); - DEBUG((DEBUG_DRIVE_IF, "returned data 0x%02x", data)); - return data; -} - - -/* Returns 0 if failed */ -static int reset_drive(void) -{ - unsigned long count = 0; - int flags; - - DEBUG((DEBUG_DRIVE_IF, "reset drive")); - - outb(0, RESET_PORT); - while (++count < RESET_WAIT) - inb(DATA_PORT); - - count = 0; - while ((flags = (inb(STATUS_PORT) & FL_RESET)) != FL_RESET) - if (++count >= BUSY_TIMEOUT) - break; - - DEBUG((DEBUG_DRIVE_IF, "reset %s", - flags == FL_RESET ? "succeeded" : "failed")); - - if (flags != FL_RESET) - return 0; /* Reset failed */ - outb(HCON_SDRQB, HCON_PORT); /* Disable Suspend Data Transfer */ - return 1; /* Reset succeeded */ -} - - -/* Facilities for asynchronous operation */ - -/* Read status/data availability flags FL_STEN and FL_DTEN */ -static inline int stdt_flags(void) -{ - return inb(STATUS_PORT) & FL_STDT; -} - - -/* Fetch status that has previously been waited for. <0 means not available */ -static inline int fetch_status(void) -{ - unsigned char status; - - if (inb(STATUS_PORT) & FL_STEN) - return -ERR_IF_NOSTAT; - - status = inb(DATA_PORT); - DEBUG((DEBUG_DRIVE_IF, "fetched exec status 0x%02x", status)); - return status; -} - - -/* Fetch data that has previously been waited for. */ -static inline void fetch_data(char *buf, int n) -{ - insb(DATA_PORT, buf, n); - DEBUG((DEBUG_DRIVE_IF, "fetched 0x%x bytes", n)); -} - - -/* Flush status and data fifos */ -static inline void flush_data(void) -{ - while ((inb(STATUS_PORT) & FL_STDT) != FL_STDT) - inb(DATA_PORT); - DEBUG((DEBUG_DRIVE_IF, "flushed fifos")); -} - -/* Command protocol */ - - -/* Send a simple command and wait for response. Command codes < COMFETCH - are quick response commands */ -static inline int exec_cmd(int cmd) -{ - int ack = send_cmd(cmd); - if (ack < 0) - return ack; - return get_exec_status(cmd < COMFETCH); -} - - -/* Send a command with parameters. Don't wait for the response, - * which consists of data blocks read from the CD. */ -static inline int exec_read_cmd(int cmd, struct cdrom_msf *params) -{ - int ack = send_cmd(cmd); - if (ack < 0) - return ack; - return send_params(params); -} - - -/* Send a seek command with parameters and wait for response */ -static inline int exec_seek_cmd(int cmd, struct cdrom_msf *params) -{ - int ack = send_cmd(cmd); - if (ack < 0) - return ack; - ack = send_seek_params(params); - if (ack < 0) - return ack; - return 0; -} - - -/* Send a command with parameters and wait for response */ -static inline int exec_long_cmd(int cmd, struct cdrom_msf *params) -{ - int ack = exec_read_cmd(cmd, params); - if (ack < 0) - return ack; - return get_exec_status(0); -} - -/* Address conversion routines */ - - -/* Binary to BCD (2 digits) */ -static inline void single_bin2bcd(u_char *p) -{ - DEBUG((DEBUG_CONV, "bin2bcd %02d", *p)); - *p = (*p % 10) | ((*p / 10) << 4); -} - - -/* Convert entire msf struct */ -static void bin2bcd(struct cdrom_msf *msf) -{ - single_bin2bcd(&msf->cdmsf_min0); - single_bin2bcd(&msf->cdmsf_sec0); - single_bin2bcd(&msf->cdmsf_frame0); - single_bin2bcd(&msf->cdmsf_min1); - single_bin2bcd(&msf->cdmsf_sec1); - single_bin2bcd(&msf->cdmsf_frame1); -} - - -/* Linear block address to minute, second, frame form */ -#define CD_FPM (CD_SECS * CD_FRAMES) /* frames per minute */ - -static void lba2msf(int lba, struct cdrom_msf *msf) -{ - DEBUG((DEBUG_CONV, "lba2msf %d", lba)); - lba += CD_MSF_OFFSET; - msf->cdmsf_min0 = lba / CD_FPM; lba %= CD_FPM; - msf->cdmsf_sec0 = lba / CD_FRAMES; - msf->cdmsf_frame0 = lba % CD_FRAMES; - msf->cdmsf_min1 = 0; - msf->cdmsf_sec1 = 0; - msf->cdmsf_frame1 = 0; - bin2bcd(msf); -} - - -/* Two BCD digits to binary */ -static inline u_char bcd2bin(u_char bcd) -{ - DEBUG((DEBUG_CONV, "bcd2bin %x%02x", bcd)); - return (bcd >> 4) * 10 + (bcd & 0x0f); -} - - -static void msf2lba(union cdrom_addr *addr) -{ - addr->lba = addr->msf.minute * CD_FPM - + addr->msf.second * CD_FRAMES - + addr->msf.frame - CD_MSF_OFFSET; -} - - -/* Minute, second, frame address BCD to binary or to linear address, - depending on MODE */ -static void msf_bcd2bin(union cdrom_addr *addr) -{ - addr->msf.minute = bcd2bin(addr->msf.minute); - addr->msf.second = bcd2bin(addr->msf.second); - addr->msf.frame = bcd2bin(addr->msf.frame); -} - -/* High level drive commands */ - - -static int audio_status = CDROM_AUDIO_NO_STATUS; -static char toc_uptodate = 0; -static char disk_changed = 1; - -/* Get drive status, flagging completion of audio play and disk changes. */ -static int drive_status(void) -{ - int status; - - status = exec_cmd(COMIOCTLISTAT); - DEBUG((DEBUG_DRIVE_IF, "IOCTLISTAT: %03x", status)); - if (status < 0) - return status; - if (status == 0xff) /* No status available */ - return -ERR_IF_NOSTAT; - - if (((status & ST_MODE_BITS) != ST_M_AUDIO) && - (audio_status == CDROM_AUDIO_PLAY)) { - audio_status = CDROM_AUDIO_COMPLETED; - } - - if (status & ST_DSK_CHG) { - toc_uptodate = 0; - disk_changed = 1; - audio_status = CDROM_AUDIO_NO_STATUS; - } - - return status; -} - - -/* Read the current Q-channel info. Also used for reading the - table of contents. qp->cdsc_format must be set on entry to - indicate the desired address format */ -static int get_q_channel(struct cdrom_subchnl *qp) -{ - int status, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10; - - status = drive_status(); - if (status < 0) - return status; - qp->cdsc_audiostatus = audio_status; - - status = exec_cmd(COMSUBQ); - if (status < 0) - return status; - - d1 = get_data(0); - if (d1 < 0) - return d1; - qp->cdsc_adr = d1; - qp->cdsc_ctrl = d1 >> 4; - - d2 = get_data(0); - if (d2 < 0) - return d2; - qp->cdsc_trk = bcd2bin(d2); - - d3 = get_data(0); - if (d3 < 0) - return d3; - qp->cdsc_ind = bcd2bin(d3); - - d4 = get_data(0); - if (d4 < 0) - return d4; - qp->cdsc_reladdr.msf.minute = d4; - - d5 = get_data(0); - if (d5 < 0) - return d5; - qp->cdsc_reladdr.msf.second = d5; - - d6 = get_data(0); - if (d6 < 0) - return d6; - qp->cdsc_reladdr.msf.frame = d6; - - d7 = get_data(0); - if (d7 < 0) - return d7; - /* byte not used */ - - d8 = get_data(0); - if (d8 < 0) - return d8; - qp->cdsc_absaddr.msf.minute = d8; - - d9 = get_data(0); - if (d9 < 0) - return d9; - qp->cdsc_absaddr.msf.second = d9; - - d10 = get_data(0); - if (d10 < 0) - return d10; - qp->cdsc_absaddr.msf.frame = d10; - - DEBUG((DEBUG_TOC, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", - d1, d2, d3, d4, d5, d6, d7, d8, d9, d10)); - - msf_bcd2bin(&qp->cdsc_absaddr); - msf_bcd2bin(&qp->cdsc_reladdr); - if (qp->cdsc_format == CDROM_LBA) { - msf2lba(&qp->cdsc_absaddr); - msf2lba(&qp->cdsc_reladdr); - } - - return 0; -} - -/* Table of contents handling */ - - -/* Errors in table of contents */ -#define ERR_TOC_MISSINGINFO 0x120 -#define ERR_TOC_MISSINGENTRY 0x121 - - -struct cdrom_disk_info { - unsigned char first; - unsigned char last; - struct cdrom_msf0 disk_length; - struct cdrom_msf0 first_track; - /* Multisession info: */ - unsigned char next; - struct cdrom_msf0 next_session; - struct cdrom_msf0 last_session; - unsigned char multi; - unsigned char xa; - unsigned char audio; -}; -static struct cdrom_disk_info disk_info; - -#define MAX_TRACKS 111 -static struct cdrom_subchnl toc[MAX_TRACKS]; - -#define QINFO_FIRSTTRACK 100 /* bcd2bin(0xa0) */ -#define QINFO_LASTTRACK 101 /* bcd2bin(0xa1) */ -#define QINFO_DISKLENGTH 102 /* bcd2bin(0xa2) */ -#define QINFO_NEXTSESSION 110 /* bcd2bin(0xb0) */ - -#define I_FIRSTTRACK 0x01 -#define I_LASTTRACK 0x02 -#define I_DISKLENGTH 0x04 -#define I_NEXTSESSION 0x08 -#define I_ALL (I_FIRSTTRACK | I_LASTTRACK | I_DISKLENGTH) - - -#if DEBUG_TOC -static void toc_debug_info(int i) -{ - printk(KERN_DEBUG "#%3d ctl %1x, adr %1x, track %2d index %3d" - " %2d:%02d.%02d %2d:%02d.%02d\n", - i, toc[i].cdsc_ctrl, toc[i].cdsc_adr, - toc[i].cdsc_trk, toc[i].cdsc_ind, - toc[i].cdsc_reladdr.msf.minute, - toc[i].cdsc_reladdr.msf.second, - toc[i].cdsc_reladdr.msf.frame, - toc[i].cdsc_absaddr.msf.minute, - toc[i].cdsc_absaddr.msf.second, - toc[i].cdsc_absaddr.msf.frame); -} -#endif - - -static int read_toc(void) -{ - int status, limit, count; - unsigned char got_info = 0; - struct cdrom_subchnl q_info; -#if DEBUG_TOC - int i; -#endif - - DEBUG((DEBUG_TOC, "starting read_toc")); - - count = 0; - for (limit = 60; limit > 0; limit--) { - int index; - - q_info.cdsc_format = CDROM_MSF; - status = get_q_channel(&q_info); - if (status < 0) - return status; - - index = q_info.cdsc_ind; - if (index > 0 && index < MAX_TRACKS - && q_info.cdsc_trk == 0 && toc[index].cdsc_ind == 0) { - toc[index] = q_info; - DEBUG((DEBUG_TOC, "got %d", index)); - if (index < 100) - count++; - - switch (q_info.cdsc_ind) { - case QINFO_FIRSTTRACK: - got_info |= I_FIRSTTRACK; - break; - case QINFO_LASTTRACK: - got_info |= I_LASTTRACK; - break; - case QINFO_DISKLENGTH: - got_info |= I_DISKLENGTH; - break; - case QINFO_NEXTSESSION: - got_info |= I_NEXTSESSION; - break; - } - } - - if ((got_info & I_ALL) == I_ALL - && toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute + count - >= toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute + 1) - break; - } - - /* Construct disk_info from TOC */ - if (disk_info.first == 0) { - disk_info.first = toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute; - disk_info.first_track.minute = - toc[disk_info.first].cdsc_absaddr.msf.minute; - disk_info.first_track.second = - toc[disk_info.first].cdsc_absaddr.msf.second; - disk_info.first_track.frame = - toc[disk_info.first].cdsc_absaddr.msf.frame; - } - disk_info.last = toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute; - disk_info.disk_length.minute = - toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.minute; - disk_info.disk_length.second = - toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.second-2; - disk_info.disk_length.frame = - toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.frame; - disk_info.next_session.minute = - toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.minute; - disk_info.next_session.second = - toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.second; - disk_info.next_session.frame = - toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.frame; - disk_info.next = toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute; - disk_info.last_session.minute = - toc[disk_info.next].cdsc_absaddr.msf.minute; - disk_info.last_session.second = - toc[disk_info.next].cdsc_absaddr.msf.second; - disk_info.last_session.frame = - toc[disk_info.next].cdsc_absaddr.msf.frame; - toc[disk_info.last + 1].cdsc_absaddr.msf.minute = - disk_info.disk_length.minute; - toc[disk_info.last + 1].cdsc_absaddr.msf.second = - disk_info.disk_length.second; - toc[disk_info.last + 1].cdsc_absaddr.msf.frame = - disk_info.disk_length.frame; -#if DEBUG_TOC - for (i = 1; i <= disk_info.last + 1; i++) - toc_debug_info(i); - toc_debug_info(QINFO_FIRSTTRACK); - toc_debug_info(QINFO_LASTTRACK); - toc_debug_info(QINFO_DISKLENGTH); - toc_debug_info(QINFO_NEXTSESSION); -#endif - - DEBUG((DEBUG_TOC, "exiting read_toc, got_info %x, count %d", - got_info, count)); - if ((got_info & I_ALL) != I_ALL - || toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute + count - < toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute + 1) - return -ERR_TOC_MISSINGINFO; - return 0; -} - - -#ifdef MULTISESSION -static int get_multi_disk_info(void) -{ - int sessions, status; - struct cdrom_msf multi_index; - - - for (sessions = 2; sessions < 10 /* %%for now */; sessions++) { - int count; - - for (count = 100; count < MAX_TRACKS; count++) - toc[count].cdsc_ind = 0; - - multi_index.cdmsf_min0 = disk_info.next_session.minute; - multi_index.cdmsf_sec0 = disk_info.next_session.second; - multi_index.cdmsf_frame0 = disk_info.next_session.frame; - if (multi_index.cdmsf_sec0 >= 20) - multi_index.cdmsf_sec0 -= 20; - else { - multi_index.cdmsf_sec0 += 40; - multi_index.cdmsf_min0--; - } - DEBUG((DEBUG_MULTIS, "Try %d: %2d:%02d.%02d", sessions, - multi_index.cdmsf_min0, - multi_index.cdmsf_sec0, - multi_index.cdmsf_frame0)); - bin2bcd(&multi_index); - multi_index.cdmsf_min1 = 0; - multi_index.cdmsf_sec1 = 0; - multi_index.cdmsf_frame1 = 1; - - status = exec_read_cmd(COMREAD, &multi_index); - if (status < 0) { - DEBUG((DEBUG_TOC, "exec_read_cmd COMREAD: %02x", - -status)); - break; - } - status = sleep_flag_low(FL_DTEN, MULTI_SEEK_TIMEOUT) ? - 0 : -ERR_TOC_MISSINGINFO; - flush_data(); - if (status < 0) { - DEBUG((DEBUG_TOC, "sleep_flag_low: %02x", -status)); - break; - } - - status = read_toc(); - if (status < 0) { - DEBUG((DEBUG_TOC, "read_toc: %02x", -status)); - break; - } - - disk_info.multi = 1; - } - - exec_cmd(COMSTOP); - - if (status < 0) - return -EIO; - return 0; -} -#endif /* MULTISESSION */ - - -static int update_toc(void) -{ - int status, count; - - if (toc_uptodate) - return 0; - - DEBUG((DEBUG_TOC, "starting update_toc")); - - disk_info.first = 0; - for (count = 0; count < MAX_TRACKS; count++) - toc[count].cdsc_ind = 0; - - status = exec_cmd(COMLEADIN); - if (status < 0) - return -EIO; - - status = read_toc(); - if (status < 0) { - DEBUG((DEBUG_TOC, "read_toc: %02x", -status)); - return -EIO; - } - - /* Audio disk detection. Look at first track. */ - disk_info.audio = - (toc[disk_info.first].cdsc_ctrl & CDROM_DATA_TRACK) ? 0 : 1; - - /* XA detection */ - disk_info.xa = drive_status() & ST_MODE2TRACK; - - /* Multisession detection: if we want this, define MULTISESSION */ - disk_info.multi = 0; -#ifdef MULTISESSION - if (disk_info.xa) - get_multi_disk_info(); /* Here disk_info.multi is set */ -#endif /* MULTISESSION */ - if (disk_info.multi) - printk(KERN_WARNING "optcd: Multisession support experimental, " - "see Documentation/cdrom/optcd\n"); - - DEBUG((DEBUG_TOC, "exiting update_toc")); - - toc_uptodate = 1; - return 0; -} - -/* Request handling */ - -static int current_valid(void) -{ - return CURRENT && - CURRENT->cmd == READ && - CURRENT->sector != -1; -} - -/* Buffers for block size conversion. */ -#define NOBUF -1 - -static char buf[CD_FRAMESIZE * N_BUFS]; -static volatile int buf_bn[N_BUFS], next_bn; -static volatile int buf_in = 0, buf_out = NOBUF; - -static inline void opt_invalidate_buffers(void) -{ - int i; - - DEBUG((DEBUG_BUFFERS, "executing opt_invalidate_buffers")); - - for (i = 0; i < N_BUFS; i++) - buf_bn[i] = NOBUF; - buf_out = NOBUF; -} - - -/* Take care of the different block sizes between cdrom and Linux. - When Linux gets variable block sizes this will probably go away. */ -static void transfer(void) -{ -#if DEBUG_BUFFERS | DEBUG_REQUEST - printk(KERN_DEBUG "optcd: executing transfer\n"); -#endif - - if (!current_valid()) - return; - while (CURRENT -> nr_sectors) { - int bn = CURRENT -> sector / 4; - int i, offs, nr_sectors; - for (i = 0; i < N_BUFS && buf_bn[i] != bn; ++i); - - DEBUG((DEBUG_REQUEST, "found %d", i)); - - if (i >= N_BUFS) { - buf_out = NOBUF; - break; - } - - offs = (i * 4 + (CURRENT -> sector & 3)) * 512; - nr_sectors = 4 - (CURRENT -> sector & 3); - - if (buf_out != i) { - buf_out = i; - if (buf_bn[i] != bn) { - buf_out = NOBUF; - continue; - } - } - - if (nr_sectors > CURRENT -> nr_sectors) - nr_sectors = CURRENT -> nr_sectors; - memcpy(CURRENT -> buffer, buf + offs, nr_sectors * 512); - CURRENT -> nr_sectors -= nr_sectors; - CURRENT -> sector += nr_sectors; - CURRENT -> buffer += nr_sectors * 512; - } -} - - -/* State machine for reading disk blocks */ - -enum state_e { - S_IDLE, /* 0 */ - S_START, /* 1 */ - S_READ, /* 2 */ - S_DATA, /* 3 */ - S_STOP, /* 4 */ - S_STOPPING /* 5 */ -}; - -static volatile enum state_e state = S_IDLE; -#if DEBUG_STATE -static volatile enum state_e state_old = S_STOP; -static volatile int flags_old = 0; -static volatile long state_n = 0; -#endif - - -/* Used as mutex to keep do_optcd_request (and other processes calling - ioctl) out while some process is inside a VFS call. - Reverse is accomplished by checking if state = S_IDLE upon entry - of opt_ioctl and opt_media_change. */ -static int in_vfs = 0; - - -static volatile int transfer_is_active = 0; -static volatile int error = 0; /* %% do something with this?? */ -static int tries; /* ibid?? */ -static int timeout = 0; - -static void poll(unsigned long data); -static struct timer_list req_timer = {.function = poll}; - - -static void poll(unsigned long data) -{ - static volatile int read_count = 1; - int flags; - int loop_again = 1; - int status = 0; - int skip = 0; - - if (error) { - printk(KERN_ERR "optcd: I/O error 0x%02x\n", error); - opt_invalidate_buffers(); - if (!tries--) { - printk(KERN_ERR "optcd: read block %d failed;" - " Giving up\n", next_bn); - if (transfer_is_active) - loop_again = 0; - if (current_valid()) - end_request(CURRENT, 0); - tries = 5; - } - error = 0; - state = S_STOP; - } - - while (loop_again) - { - loop_again = 0; /* each case must flip this back to 1 if we want - to come back up here */ - -#if DEBUG_STATE - if (state == state_old) - state_n++; - else { - state_old = state; - if (++state_n > 1) - printk(KERN_DEBUG "optcd: %ld times " - "in previous state\n", state_n); - printk(KERN_DEBUG "optcd: state %d\n", state); - state_n = 0; - } -#endif - - switch (state) { - case S_IDLE: - return; - case S_START: - if (in_vfs) - break; - if (send_cmd(COMDRVST)) { - state = S_IDLE; - while (current_valid()) - end_request(CURRENT, 0); - return; - } - state = S_READ; - timeout = READ_TIMEOUT; - break; - case S_READ: { - struct cdrom_msf msf; - if (!skip) { - status = fetch_status(); - if (status < 0) - break; - if (status & ST_DSK_CHG) { - toc_uptodate = 0; - opt_invalidate_buffers(); - } - } - skip = 0; - if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) { - toc_uptodate = 0; - opt_invalidate_buffers(); - printk(KERN_WARNING "optcd: %s\n", - (status & ST_DOOR_OPEN) - ? "door open" - : "disk removed"); - state = S_IDLE; - while (current_valid()) - end_request(CURRENT, 0); - return; - } - if (!current_valid()) { - state = S_STOP; - loop_again = 1; - break; - } - next_bn = CURRENT -> sector / 4; - lba2msf(next_bn, &msf); - read_count = N_BUFS; - msf.cdmsf_frame1 = read_count; /* Not BCD! */ - - DEBUG((DEBUG_REQUEST, "reading %x:%x.%x %x:%x.%x", - msf.cdmsf_min0, - msf.cdmsf_sec0, - msf.cdmsf_frame0, - msf.cdmsf_min1, - msf.cdmsf_sec1, - msf.cdmsf_frame1)); - DEBUG((DEBUG_REQUEST, "next_bn:%d buf_in:%d" - " buf_out:%d buf_bn:%d", - next_bn, - buf_in, - buf_out, - buf_bn[buf_in])); - - exec_read_cmd(COMREAD, &msf); - state = S_DATA; - timeout = READ_TIMEOUT; - break; - } - case S_DATA: - flags = stdt_flags() & (FL_STEN|FL_DTEN); - -#if DEBUG_STATE - if (flags != flags_old) { - flags_old = flags; - printk(KERN_DEBUG "optcd: flags:%x\n", flags); - } - if (flags == FL_STEN) - printk(KERN_DEBUG "timeout cnt: %d\n", timeout); -#endif - - switch (flags) { - case FL_DTEN: /* only STEN low */ - if (!tries--) { - printk(KERN_ERR - "optcd: read block %d failed; " - "Giving up\n", next_bn); - if (transfer_is_active) { - tries = 0; - break; - } - if (current_valid()) - end_request(CURRENT, 0); - tries = 5; - } - state = S_START; - timeout = READ_TIMEOUT; - loop_again = 1; - case (FL_STEN|FL_DTEN): /* both high */ - break; - default: /* DTEN low */ - tries = 5; - if (!current_valid() && buf_in == buf_out) { - state = S_STOP; - loop_again = 1; - break; - } - if (read_count<=0) - printk(KERN_WARNING - "optcd: warning - try to read" - " 0 frames\n"); - while (read_count) { - buf_bn[buf_in] = NOBUF; - if (!flag_low(FL_DTEN, BUSY_TIMEOUT)) { - /* should be no waiting here!?? */ - printk(KERN_ERR - "read_count:%d " - "CURRENT->nr_sectors:%ld " - "buf_in:%d\n", - read_count, - CURRENT->nr_sectors, - buf_in); - printk(KERN_ERR - "transfer active: %x\n", - transfer_is_active); - read_count = 0; - state = S_STOP; - loop_again = 1; - end_request(CURRENT, 0); - break; - } - fetch_data(buf+ - CD_FRAMESIZE*buf_in, - CD_FRAMESIZE); - read_count--; - - DEBUG((DEBUG_REQUEST, - "S_DATA; ---I've read data- " - "read_count: %d", - read_count)); - DEBUG((DEBUG_REQUEST, - "next_bn:%d buf_in:%d " - "buf_out:%d buf_bn:%d", - next_bn, - buf_in, - buf_out, - buf_bn[buf_in])); - - buf_bn[buf_in] = next_bn++; - if (buf_out == NOBUF) - buf_out = buf_in; - buf_in = buf_in + 1 == - N_BUFS ? 0 : buf_in + 1; - } - if (!transfer_is_active) { - while (current_valid()) { - transfer(); - if (CURRENT -> nr_sectors == 0) - end_request(CURRENT, 1); - else - break; - } - } - - if (current_valid() - && (CURRENT -> sector / 4 < next_bn || - CURRENT -> sector / 4 > - next_bn + N_BUFS)) { - state = S_STOP; - loop_again = 1; - break; - } - timeout = READ_TIMEOUT; - if (read_count == 0) { - state = S_STOP; - loop_again = 1; - break; - } - } - break; - case S_STOP: - if (read_count != 0) - printk(KERN_ERR - "optcd: discard data=%x frames\n", - read_count); - flush_data(); - if (send_cmd(COMDRVST)) { - state = S_IDLE; - while (current_valid()) - end_request(CURRENT, 0); - return; - } - state = S_STOPPING; - timeout = STOP_TIMEOUT; - break; - case S_STOPPING: - status = fetch_status(); - if (status < 0 && timeout) - break; - if ((status >= 0) && (status & ST_DSK_CHG)) { - toc_uptodate = 0; - opt_invalidate_buffers(); - } - if (current_valid()) { - if (status >= 0) { - state = S_READ; - loop_again = 1; - skip = 1; - break; - } else { - state = S_START; - timeout = 1; - } - } else { - state = S_IDLE; - return; - } - break; - default: - printk(KERN_ERR "optcd: invalid state %d\n", state); - return; - } /* case */ - } /* while */ - - if (!timeout--) { - printk(KERN_ERR "optcd: timeout in state %d\n", state); - state = S_STOP; - if (exec_cmd(COMSTOP) < 0) { - state = S_IDLE; - while (current_valid()) - end_request(CURRENT, 0); - return; - } - } - - mod_timer(&req_timer, jiffies + HZ/100); -} - - -static void do_optcd_request(request_queue_t * q) -{ - DEBUG((DEBUG_REQUEST, "do_optcd_request(%ld+%ld)", - CURRENT -> sector, CURRENT -> nr_sectors)); - - if (disk_info.audio) { - printk(KERN_WARNING "optcd: tried to mount an Audio CD\n"); - end_request(CURRENT, 0); - return; - } - - transfer_is_active = 1; - while (current_valid()) { - transfer(); /* First try to transfer block from buffers */ - if (CURRENT -> nr_sectors == 0) { - end_request(CURRENT, 1); - } else { /* Want to read a block not in buffer */ - buf_out = NOBUF; - if (state == S_IDLE) { - /* %% Should this block the request queue?? */ - if (update_toc() < 0) { - while (current_valid()) - end_request(CURRENT, 0); - break; - } - /* Start state machine */ - state = S_START; - timeout = READ_TIMEOUT; - tries = 5; - /* %% why not start right away?? */ - mod_timer(&req_timer, jiffies + HZ/100); - } - break; - } - } - transfer_is_active = 0; - - DEBUG((DEBUG_REQUEST, "next_bn:%d buf_in:%d buf_out:%d buf_bn:%d", - next_bn, buf_in, buf_out, buf_bn[buf_in])); - DEBUG((DEBUG_REQUEST, "do_optcd_request ends")); -} - -/* IOCTLs */ - - -static char auto_eject = 0; - -static int cdrompause(void) -{ - int status; - - if (audio_status != CDROM_AUDIO_PLAY) - return -EINVAL; - - status = exec_cmd(COMPAUSEON); - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_cmd COMPAUSEON: %02x", -status)); - return -EIO; - } - audio_status = CDROM_AUDIO_PAUSED; - return 0; -} - - -static int cdromresume(void) -{ - int status; - - if (audio_status != CDROM_AUDIO_PAUSED) - return -EINVAL; - - status = exec_cmd(COMPAUSEOFF); - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_cmd COMPAUSEOFF: %02x", -status)); - audio_status = CDROM_AUDIO_ERROR; - return -EIO; - } - audio_status = CDROM_AUDIO_PLAY; - return 0; -} - - -static int cdromplaymsf(void __user *arg) -{ - int status; - struct cdrom_msf msf; - - if (copy_from_user(&msf, arg, sizeof msf)) - return -EFAULT; - - bin2bcd(&msf); - status = exec_long_cmd(COMPLAY, &msf); - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_long_cmd COMPLAY: %02x", -status)); - audio_status = CDROM_AUDIO_ERROR; - return -EIO; - } - - audio_status = CDROM_AUDIO_PLAY; - return 0; -} - - -static int cdromplaytrkind(void __user *arg) -{ - int status; - struct cdrom_ti ti; - struct cdrom_msf msf; - - if (copy_from_user(&ti, arg, sizeof ti)) - return -EFAULT; - - if (ti.cdti_trk0 < disk_info.first - || ti.cdti_trk0 > disk_info.last - || ti.cdti_trk1 < ti.cdti_trk0) - return -EINVAL; - if (ti.cdti_trk1 > disk_info.last) - ti.cdti_trk1 = disk_info.last; - - msf.cdmsf_min0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.minute; - msf.cdmsf_sec0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.second; - msf.cdmsf_frame0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.frame; - msf.cdmsf_min1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.minute; - msf.cdmsf_sec1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.second; - msf.cdmsf_frame1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.frame; - - DEBUG((DEBUG_VFS, "play %02d:%02d.%02d to %02d:%02d.%02d", - msf.cdmsf_min0, - msf.cdmsf_sec0, - msf.cdmsf_frame0, - msf.cdmsf_min1, - msf.cdmsf_sec1, - msf.cdmsf_frame1)); - - bin2bcd(&msf); - status = exec_long_cmd(COMPLAY, &msf); - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_long_cmd COMPLAY: %02x", -status)); - audio_status = CDROM_AUDIO_ERROR; - return -EIO; - } - - audio_status = CDROM_AUDIO_PLAY; - return 0; -} - - -static int cdromreadtochdr(void __user *arg) -{ - struct cdrom_tochdr tochdr; - - tochdr.cdth_trk0 = disk_info.first; - tochdr.cdth_trk1 = disk_info.last; - - return copy_to_user(arg, &tochdr, sizeof tochdr) ? -EFAULT : 0; -} - - -static int cdromreadtocentry(void __user *arg) -{ - struct cdrom_tocentry entry; - struct cdrom_subchnl *tocptr; - - if (copy_from_user(&entry, arg, sizeof entry)) - return -EFAULT; - - if (entry.cdte_track == CDROM_LEADOUT) - tocptr = &toc[disk_info.last + 1]; - else if (entry.cdte_track > disk_info.last - || entry.cdte_track < disk_info.first) - return -EINVAL; - else - tocptr = &toc[entry.cdte_track]; - - entry.cdte_adr = tocptr->cdsc_adr; - entry.cdte_ctrl = tocptr->cdsc_ctrl; - entry.cdte_addr.msf.minute = tocptr->cdsc_absaddr.msf.minute; - entry.cdte_addr.msf.second = tocptr->cdsc_absaddr.msf.second; - entry.cdte_addr.msf.frame = tocptr->cdsc_absaddr.msf.frame; - /* %% What should go into entry.cdte_datamode? */ - - if (entry.cdte_format == CDROM_LBA) - msf2lba(&entry.cdte_addr); - else if (entry.cdte_format != CDROM_MSF) - return -EINVAL; - - return copy_to_user(arg, &entry, sizeof entry) ? -EFAULT : 0; -} - - -static int cdromvolctrl(void __user *arg) -{ - int status; - struct cdrom_volctrl volctrl; - struct cdrom_msf msf; - - if (copy_from_user(&volctrl, arg, sizeof volctrl)) - return -EFAULT; - - msf.cdmsf_min0 = 0x10; - msf.cdmsf_sec0 = 0x32; - msf.cdmsf_frame0 = volctrl.channel0; - msf.cdmsf_min1 = volctrl.channel1; - msf.cdmsf_sec1 = volctrl.channel2; - msf.cdmsf_frame1 = volctrl.channel3; - - status = exec_long_cmd(COMCHCTRL, &msf); - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_long_cmd COMCHCTRL: %02x", -status)); - return -EIO; - } - return 0; -} - - -static int cdromsubchnl(void __user *arg) -{ - int status; - struct cdrom_subchnl subchnl; - - if (copy_from_user(&subchnl, arg, sizeof subchnl)) - return -EFAULT; - - if (subchnl.cdsc_format != CDROM_LBA - && subchnl.cdsc_format != CDROM_MSF) - return -EINVAL; - - status = get_q_channel(&subchnl); - if (status < 0) { - DEBUG((DEBUG_VFS, "get_q_channel: %02x", -status)); - return -EIO; - } - - if (copy_to_user(arg, &subchnl, sizeof subchnl)) - return -EFAULT; - return 0; -} - - -static struct gendisk *optcd_disk; - - -static int cdromread(void __user *arg, int blocksize, int cmd) -{ - int status; - struct cdrom_msf msf; - - if (copy_from_user(&msf, arg, sizeof msf)) - return -EFAULT; - - bin2bcd(&msf); - msf.cdmsf_min1 = 0; - msf.cdmsf_sec1 = 0; - msf.cdmsf_frame1 = 1; /* read only one frame */ - status = exec_read_cmd(cmd, &msf); - - DEBUG((DEBUG_VFS, "read cmd status 0x%x", status)); - - if (!sleep_flag_low(FL_DTEN, SLEEP_TIMEOUT)) - return -EIO; - - fetch_data(optcd_disk->private_data, blocksize); - - if (copy_to_user(arg, optcd_disk->private_data, blocksize)) - return -EFAULT; - - return 0; -} - - -static int cdromseek(void __user *arg) -{ - int status; - struct cdrom_msf msf; - - if (copy_from_user(&msf, arg, sizeof msf)) - return -EFAULT; - - bin2bcd(&msf); - status = exec_seek_cmd(COMSEEK, &msf); - - DEBUG((DEBUG_VFS, "COMSEEK status 0x%x", status)); - - if (status < 0) - return -EIO; - return 0; -} - - -#ifdef MULTISESSION -static int cdrommultisession(void __user *arg) -{ - struct cdrom_multisession ms; - - if (copy_from_user(&ms, arg, sizeof ms)) - return -EFAULT; - - ms.addr.msf.minute = disk_info.last_session.minute; - ms.addr.msf.second = disk_info.last_session.second; - ms.addr.msf.frame = disk_info.last_session.frame; - - if (ms.addr_format != CDROM_LBA - && ms.addr_format != CDROM_MSF) - return -EINVAL; - if (ms.addr_format == CDROM_LBA) - msf2lba(&ms.addr); - - ms.xa_flag = disk_info.xa; - - if (copy_to_user(arg, &ms, sizeof(struct cdrom_multisession))) - return -EFAULT; - -#if DEBUG_MULTIS - if (ms.addr_format == CDROM_MSF) - printk(KERN_DEBUG - "optcd: multisession xa:%d, msf:%02d:%02d.%02d\n", - ms.xa_flag, - ms.addr.msf.minute, - ms.addr.msf.second, - ms.addr.msf.frame); - else - printk(KERN_DEBUG - "optcd: multisession %d, lba:0x%08x [%02d:%02d.%02d])\n", - ms.xa_flag, - ms.addr.lba, - disk_info.last_session.minute, - disk_info.last_session.second, - disk_info.last_session.frame); -#endif /* DEBUG_MULTIS */ - - return 0; -} -#endif /* MULTISESSION */ - - -static int cdromreset(void) -{ - if (state != S_IDLE) { - error = 1; - tries = 0; - } - - toc_uptodate = 0; - disk_changed = 1; - opt_invalidate_buffers(); - audio_status = CDROM_AUDIO_NO_STATUS; - - if (!reset_drive()) - return -EIO; - return 0; -} - -/* VFS calls */ - - -static int opt_ioctl(struct inode *ip, struct file *fp, - unsigned int cmd, unsigned long arg) -{ - int status, err, retval = 0; - void __user *argp = (void __user *)arg; - - DEBUG((DEBUG_VFS, "starting opt_ioctl")); - - if (!ip) - return -EINVAL; - - if (cmd == CDROMRESET) - return cdromreset(); - - /* is do_optcd_request or another ioctl busy? */ - if (state != S_IDLE || in_vfs) - return -EBUSY; - - in_vfs = 1; - - status = drive_status(); - if (status < 0) { - DEBUG((DEBUG_VFS, "drive_status: %02x", -status)); - in_vfs = 0; - return -EIO; - } - - if (status & ST_DOOR_OPEN) - switch (cmd) { /* Actions that can be taken with door open */ - case CDROMCLOSETRAY: - /* We do this before trying to read the toc. */ - err = exec_cmd(COMCLOSE); - if (err < 0) { - DEBUG((DEBUG_VFS, - "exec_cmd COMCLOSE: %02x", -err)); - in_vfs = 0; - return -EIO; - } - break; - default: in_vfs = 0; - return -EBUSY; - } - - err = update_toc(); - if (err < 0) { - DEBUG((DEBUG_VFS, "update_toc: %02x", -err)); - in_vfs = 0; - return -EIO; - } - - DEBUG((DEBUG_VFS, "ioctl cmd 0x%x", cmd)); - - switch (cmd) { - case CDROMPAUSE: retval = cdrompause(); break; - case CDROMRESUME: retval = cdromresume(); break; - case CDROMPLAYMSF: retval = cdromplaymsf(argp); break; - case CDROMPLAYTRKIND: retval = cdromplaytrkind(argp); break; - case CDROMREADTOCHDR: retval = cdromreadtochdr(argp); break; - case CDROMREADTOCENTRY: retval = cdromreadtocentry(argp); break; - - case CDROMSTOP: err = exec_cmd(COMSTOP); - if (err < 0) { - DEBUG((DEBUG_VFS, - "exec_cmd COMSTOP: %02x", - -err)); - retval = -EIO; - } else - audio_status = CDROM_AUDIO_NO_STATUS; - break; - case CDROMSTART: break; /* This is a no-op */ - case CDROMEJECT: err = exec_cmd(COMUNLOCK); - if (err < 0) { - DEBUG((DEBUG_VFS, - "exec_cmd COMUNLOCK: %02x", - -err)); - retval = -EIO; - break; - } - err = exec_cmd(COMOPEN); - if (err < 0) { - DEBUG((DEBUG_VFS, - "exec_cmd COMOPEN: %02x", - -err)); - retval = -EIO; - } - break; - - case CDROMVOLCTRL: retval = cdromvolctrl(argp); break; - case CDROMSUBCHNL: retval = cdromsubchnl(argp); break; - - /* The drive detects the mode and automatically delivers the - correct 2048 bytes, so we don't need these IOCTLs */ - case CDROMREADMODE2: retval = -EINVAL; break; - case CDROMREADMODE1: retval = -EINVAL; break; - - /* Drive doesn't support reading audio */ - case CDROMREADAUDIO: retval = -EINVAL; break; - - case CDROMEJECT_SW: auto_eject = (char) arg; - break; - -#ifdef MULTISESSION - case CDROMMULTISESSION: retval = cdrommultisession(argp); break; -#endif - - case CDROM_GET_MCN: retval = -EINVAL; break; /* not implemented */ - case CDROMVOLREAD: retval = -EINVAL; break; /* not implemented */ - - case CDROMREADRAW: - /* this drive delivers 2340 bytes in raw mode */ - retval = cdromread(argp, CD_FRAMESIZE_RAW1, COMREADRAW); - break; - case CDROMREADCOOKED: - retval = cdromread(argp, CD_FRAMESIZE, COMREAD); - break; - case CDROMREADALL: - retval = cdromread(argp, CD_FRAMESIZE_RAWER, COMREADALL); - break; - - case CDROMSEEK: retval = cdromseek(argp); break; - case CDROMPLAYBLK: retval = -EINVAL; break; /* not implemented */ - case CDROMCLOSETRAY: break; /* The action was taken earlier */ - default: retval = -EINVAL; - } - in_vfs = 0; - return retval; -} - - -static int open_count = 0; - -/* Open device special file; check that a disk is in. */ -static int opt_open(struct inode *ip, struct file *fp) -{ - DEBUG((DEBUG_VFS, "starting opt_open")); - - if (!open_count && state == S_IDLE) { - int status; - char *buf; - - buf = kmalloc(CD_FRAMESIZE_RAWER, GFP_KERNEL); - if (!buf) { - printk(KERN_INFO "optcd: cannot allocate read buffer\n"); - return -ENOMEM; - } - optcd_disk->private_data = buf; /* save read buffer */ - - toc_uptodate = 0; - opt_invalidate_buffers(); - - status = exec_cmd(COMCLOSE); /* close door */ - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_cmd COMCLOSE: %02x", -status)); - } - - status = drive_status(); - if (status < 0) { - DEBUG((DEBUG_VFS, "drive_status: %02x", -status)); - goto err_out; - } - DEBUG((DEBUG_VFS, "status: %02x", status)); - if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) { - printk(KERN_INFO "optcd: no disk or door open\n"); - goto err_out; - } - status = exec_cmd(COMLOCK); /* Lock door */ - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_cmd COMLOCK: %02x", -status)); - } - status = update_toc(); /* Read table of contents */ - if (status < 0) { - DEBUG((DEBUG_VFS, "update_toc: %02x", -status)); - status = exec_cmd(COMUNLOCK); /* Unlock door */ - if (status < 0) { - DEBUG((DEBUG_VFS, - "exec_cmd COMUNLOCK: %02x", -status)); - } - goto err_out; - } - open_count++; - } - - DEBUG((DEBUG_VFS, "exiting opt_open")); - - return 0; - -err_out: - return -EIO; -} - - -/* Release device special file; flush all blocks from the buffer cache */ -static int opt_release(struct inode *ip, struct file *fp) -{ - int status; - - DEBUG((DEBUG_VFS, "executing opt_release")); - DEBUG((DEBUG_VFS, "inode: %p, device: %s, file: %p\n", - ip, ip->i_bdev->bd_disk->disk_name, fp)); - - if (!--open_count) { - toc_uptodate = 0; - opt_invalidate_buffers(); - status = exec_cmd(COMUNLOCK); /* Unlock door */ - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_cmd COMUNLOCK: %02x", -status)); - } - if (auto_eject) { - status = exec_cmd(COMOPEN); - DEBUG((DEBUG_VFS, "exec_cmd COMOPEN: %02x", -status)); - } - kfree(optcd_disk->private_data); - del_timer(&delay_timer); - del_timer(&req_timer); - } - return 0; -} - - -/* Check if disk has been changed */ -static int opt_media_change(struct gendisk *disk) -{ - DEBUG((DEBUG_VFS, "executing opt_media_change")); - DEBUG((DEBUG_VFS, "dev: %s; disk_changed = %d\n", - disk->disk_name, disk_changed)); - - if (disk_changed) { - disk_changed = 0; - return 1; - } - return 0; -} - -/* Driver initialisation */ - - -/* Returns 1 if a drive is detected with a version string - starting with "DOLPHIN". Otherwise 0. */ -static int __init version_ok(void) -{ - char devname[100]; - int count, i, ch, status; - - status = exec_cmd(COMVERSION); - if (status < 0) { - DEBUG((DEBUG_VFS, "exec_cmd COMVERSION: %02x", -status)); - return 0; - } - if ((count = get_data(1)) < 0) { - DEBUG((DEBUG_VFS, "get_data(1): %02x", -count)); - return 0; - } - for (i = 0, ch = -1; count > 0; count--) { - if ((ch = get_data(1)) < 0) { - DEBUG((DEBUG_VFS, "get_data(1): %02x", -ch)); - break; - } - if (i < 99) - devname[i++] = ch; - } - devname[i] = '\0'; - if (ch < 0) - return 0; - - printk(KERN_INFO "optcd: Device %s detected\n", devname); - return ((devname[0] == 'D') - && (devname[1] == 'O') - && (devname[2] == 'L') - && (devname[3] == 'P') - && (devname[4] == 'H') - && (devname[5] == 'I') - && (devname[6] == 'N')); -} - - -static struct block_device_operations opt_fops = { - .owner = THIS_MODULE, - .open = opt_open, - .release = opt_release, - .ioctl = opt_ioctl, - .media_changed = opt_media_change, -}; - -#ifndef MODULE -/* Get kernel parameter when used as a kernel driver */ -static int optcd_setup(char *str) -{ - int ints[4]; - (void)get_options(str, ARRAY_SIZE(ints), ints); - - if (ints[0] > 0) - optcd_port = ints[1]; - - return 1; -} - -__setup("optcd=", optcd_setup); - -#endif /* MODULE */ - -/* Test for presence of drive and initialize it. Called at boot time - or during module initialisation. */ -static int __init optcd_init(void) -{ - int status; - - if (optcd_port <= 0) { - printk(KERN_INFO - "optcd: no Optics Storage CDROM Initialization\n"); - return -EIO; - } - optcd_disk = alloc_disk(1); - if (!optcd_disk) { - printk(KERN_ERR "optcd: can't allocate disk\n"); - return -ENOMEM; - } - optcd_disk->major = MAJOR_NR; - optcd_disk->first_minor = 0; - optcd_disk->fops = &opt_fops; - sprintf(optcd_disk->disk_name, "optcd"); - - if (!request_region(optcd_port, 4, "optcd")) { - printk(KERN_ERR "optcd: conflict, I/O port 0x%x already used\n", - optcd_port); - put_disk(optcd_disk); - return -EIO; - } - - if (!reset_drive()) { - printk(KERN_ERR "optcd: drive at 0x%x not ready\n", optcd_port); - release_region(optcd_port, 4); - put_disk(optcd_disk); - return -EIO; - } - if (!version_ok()) { - printk(KERN_ERR "optcd: unknown drive detected; aborting\n"); - release_region(optcd_port, 4); - put_disk(optcd_disk); - return -EIO; - } - status = exec_cmd(COMINITDOUBLE); - if (status < 0) { - printk(KERN_ERR "optcd: cannot init double speed mode\n"); - release_region(optcd_port, 4); - DEBUG((DEBUG_VFS, "exec_cmd COMINITDOUBLE: %02x", -status)); - put_disk(optcd_disk); - return -EIO; - } - if (register_blkdev(MAJOR_NR, "optcd")) { - release_region(optcd_port, 4); - put_disk(optcd_disk); - return -EIO; - } - - - opt_queue = blk_init_queue(do_optcd_request, &optcd_lock); - if (!opt_queue) { - unregister_blkdev(MAJOR_NR, "optcd"); - release_region(optcd_port, 4); - put_disk(optcd_disk); - return -ENOMEM; - } - - blk_queue_hardsect_size(opt_queue, 2048); - optcd_disk->queue = opt_queue; - add_disk(optcd_disk); - - printk(KERN_INFO "optcd: DOLPHIN 8000 AT CDROM at 0x%x\n", optcd_port); - return 0; -} - - -static void __exit optcd_exit(void) -{ - del_gendisk(optcd_disk); - put_disk(optcd_disk); - if (unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) { - printk(KERN_ERR "optcd: what's that: can't unregister\n"); - return; - } - blk_cleanup_queue(opt_queue); - release_region(optcd_port, 4); - printk(KERN_INFO "optcd: module released.\n"); -} - -module_init(optcd_init); -module_exit(optcd_exit); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_BLOCKDEV_MAJOR(OPTICS_CDROM_MAJOR); diff --git a/drivers/cdrom/optcd.h b/drivers/cdrom/optcd.h deleted file mode 100644 index 1911bb92ee2..00000000000 --- a/drivers/cdrom/optcd.h +++ /dev/null @@ -1,52 +0,0 @@ -/* linux/include/linux/optcd.h - Optics Storage 8000 AT CDROM driver - $Id: optcd.h,v 1.2 1996/01/15 18:43:44 root Exp root $ - - Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl) - - - Configuration file for linux/drivers/cdrom/optcd.c -*/ - -#ifndef _LINUX_OPTCD_H -#define _LINUX_OPTCD_H - - -/* I/O base of drive. Drive uses base to base+2. - This setting can be overridden with the kernel or insmod command - line option 'optcd=<portbase>'. Use address of 0 to disable driver. */ -#define OPTCD_PORTBASE 0x340 - - -/* enable / disable parts of driver by define / undef */ -#define MULTISESSION /* multisession support (ALPHA) */ - - -/* Change 0 to 1 to debug various parts of the driver */ -#define DEBUG_DRIVE_IF 0 /* Low level drive interface */ -#define DEBUG_CONV 0 /* Address conversions */ -#define DEBUG_BUFFERS 0 /* Buffering and block size conversion */ -#define DEBUG_REQUEST 0 /* Request mechanism */ -#define DEBUG_STATE 0 /* State machine */ -#define DEBUG_TOC 0 /* Q-channel and Table of Contents */ -#define DEBUG_MULTIS 0 /* Multisession code */ -#define DEBUG_VFS 0 /* VFS interface */ - - -/* Don't touch these unless you know what you're doing. */ - -/* Various timeout loop repetition counts. */ -#define BUSY_TIMEOUT 10000000 /* for busy wait */ -#define FAST_TIMEOUT 100000 /* ibid. for probing */ -#define SLEEP_TIMEOUT 6000 /* for timer wait */ -#define MULTI_SEEK_TIMEOUT 1000 /* for timer wait */ -#define READ_TIMEOUT 6000 /* for poll wait */ -#define STOP_TIMEOUT 2000 /* for poll wait */ -#define RESET_WAIT 5000 /* busy wait at drive reset */ - -/* # of buffers for block size conversion. 6 is optimal for my setup (P75), - giving 280 kb/s, with 0.4% CPU usage. Experiment to find your optimal - setting */ -#define N_BUFS 6 - - -#endif /* _LINUX_OPTCD_H */ diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c deleted file mode 100644 index a1283b1ef98..00000000000 --- a/drivers/cdrom/sbpcd.c +++ /dev/null @@ -1,5966 +0,0 @@ -/* - * sbpcd.c CD-ROM device driver for the whole family of traditional, - * non-ATAPI IDE-style Matsushita/Panasonic CR-5xx drives. - * Works with SoundBlaster compatible cards and with "no-sound" - * interface cards like Lasermate, Panasonic CI-101P, Teac, ... - * Also for the Longshine LCS-7260 drive. - * Also for the IBM "External ISA CD-Rom" drive. - * Also for the CreativeLabs CD200 drive. - * Also for the TEAC CD-55A drive. - * Also for the ECS-AT "Vertos 100" drive. - * Not for Sanyo drives (but for the H94A, sjcd is there...). - * Not for any other Funai drives than the CD200 types (sometimes - * labelled E2550UA or MK4015 or 2800F). - */ - -#define VERSION "v4.63 Andrew J. Kroll <ag784@freenet.buffalo.edu> Wed Jul 26 04:24:10 EDT 2000" - -/* Copyright (C) 1993, 1994, 1995 Eberhard Moenkeberg <emoenke@gwdg.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * You should have received a copy of the GNU General Public License - * (for example /usr/src/linux/COPYING); if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * If you change this software, you should mail a .diff file with some - * description lines to emoenke@gwdg.de. I want to know about it. - * - * If you are the editor of a Linux CD, you should enable sbpcd.c within - * your boot floppy kernel and send me one of your CDs for free. - * - * If you would like to port the driver to an other operating system (f.e. - * FreeBSD or NetBSD) or use it as an information source, you shall not be - * restricted by the GPL under the following conditions: - * a) the source code of your work is freely available - * b) my part of the work gets mentioned at all places where your - * authorship gets mentioned - * c) I receive a copy of your code together with a full installation - * package of your operating system for free. - * - * - * VERSION HISTORY - * - * 0.1 initial release, April/May 93, after mcd.c (Martin Harriss) - * - * 0.2 thek "repeat:"-loop in do_sbpcd_request did not check for - * end-of-request_queue (resulting in kernel panic). - * Flow control seems stable, but throughput is not better. - * - * 0.3 interrupt locking totally eliminated (maybe "inb" and "outb" - * are still locking) - 0.2 made keyboard-type-ahead losses. - * check_sbpcd_media_change added (to use by isofs/inode.c) - * - but it detects almost nothing. - * - * 0.4 use MAJOR 25 definitely. - * Almost total re-design to support double-speed drives and - * "naked" (no sound) interface cards ("LaserMate" interface type). - * Flow control should be exact now. - * Don't occupy the SbPro IRQ line (not needed either); will - * live together with Hannu Savolainen's sndkit now. - * Speeded up data transfer to 150 kB/sec, with help from Kai - * Makisara, the "provider" of the "mt" tape utility. - * Give "SpinUp" command if necessary. - * First steps to support up to 4 drives (but currently only one). - * Implemented audio capabilities - workman should work, xcdplayer - * gives some problems. - * This version is still consuming too much CPU time, and - * sleeping still has to be worked on. - * During "long" implied seeks, it seems possible that a - * ReadStatus command gets ignored. That gives the message - * "ResponseStatus timed out" (happens about 6 times here during - * a "ls -alR" of the YGGDRASIL LGX-Beta CD). Such a case is - * handled without data error, but it should get done better. - * - * 0.5 Free CPU during waits (again with help from Kai Makisara). - * Made it work together with the LILO/kernel setup standard. - * Included auto-probing code, as suggested by YGGDRASIL. - * Formal redesign to add DDI debugging. - * There are still flaws in IOCTL (workman with double speed drive). - * - * 1.0 Added support for all drive IDs (0...3, no longer only 0) - * and up to 4 drives on one controller. - * Added "#define MANY_SESSION" for "old" multi session CDs. - * - * 1.1 Do SpinUp for new drives, too. - * Revised for clean compile under "old" kernels (0.99pl9). - * - * 1.2 Found the "workman with double-speed drive" bug: use the driver's - * audio_state, not what the drive is reporting with ReadSubQ. - * - * 1.3 Minor cleanups. - * Refinements regarding Workman. - * - * 1.4 Read XA disks (PhotoCDs) with "old" drives, too (but only the first - * session - no chance to fully access a "multi-session" CD). - * This currently still is too slow (50 kB/sec) - but possibly - * the old drives won't do it faster. - * Implemented "door (un)lock" for new drives (still does not work - * as wanted - no lock possible after an unlock). - * Added some debugging printout for the UPC/EAN code - but my drives - * return only zeroes. Is there no UPC/EAN code written? - * - * 1.5 Laborate with UPC/EAN code (not better yet). - * Adapt to kernel 1.1.8 change (have to explicitly include - * <linux/string.h> now). - * - * 1.6 Trying to read audio frames as data. Impossible with the current - * drive firmware levels, as it seems. Awaiting any hint. ;-) - * Changed "door unlock": repeat it until success. - * Changed CDROMSTOP routine (stop somewhat "softer" so that Workman - * won't get confused). - * Added a third interface type: Sequoia S-1000, as used with the SPEA - * Media FX sound card. This interface (usable for Sony and Mitsumi - * drives, too) needs a special configuration setup and behaves like a - * LaserMate type after that. Still experimental - I do not have such - * an interface. - * Use the "variable BLOCK_SIZE" feature (2048). But it does only work - * if you give the mount option "block=2048". - * The media_check routine is currently disabled; now that it gets - * called as it should I fear it must get synchronized for not to - * disturb the normal driver's activity. - * - * 2.0 Version number bumped - two reasons: - * - reading audio tracks as data works now with CR-562 and CR-563. We - * currently do it by an IOCTL (yet has to get standardized), one frame - * at a time; that is pretty slow. But it works. - * - we are maintaining now up to 4 interfaces (each up to 4 drives): - * did it the easy way - a different MAJOR (25, 26, ...) and a different - * copy of the driver (sbpcd.c, sbpcd2.c, sbpcd3.c, sbpcd4.c - only - * distinguished by the value of SBPCD_ISSUE and the driver's name), - * and a common sbpcd.h file. - * Bettered the "ReadCapacity error" problem with old CR-52x drives (the - * drives sometimes need a manual "eject/insert" before work): just - * reset the drive and do again. Needs lots of resets here and sometimes - * that does not cure, so this can't be the solution. - * - * 2.1 Found bug with multisession CDs (accessing frame 16). - * "read audio" works now with address type CDROM_MSF, too. - * Bigger audio frame buffer: allows reading max. 4 frames at time; this - * gives a significant speedup, but reading more than one frame at once - * gives missing chunks at each single frame boundary. - * - * 2.2 Kernel interface cleanups: timers, init, setup, media check. - * - * 2.3 Let "door lock" and "eject" live together. - * Implemented "close tray" (done automatically during open). - * - * 2.4 Use different names for device registering. - * - * 2.5 Added "#if EJECT" code (default: enabled) to automatically eject - * the tray during last call to "sbpcd_release". - * Added "#if JUKEBOX" code (default: disabled) to automatically eject - * the tray during call to "sbpcd_open" if no disk is in. - * Turn on the CD volume of "compatible" sound cards, too; just define - * SOUND_BASE (in sbpcd.h) accordingly (default: disabled). - * - * 2.6 Nothing new. - * - * 2.7 Added CDROMEJECT_SW ioctl to set the "EJECT" behavior on the fly: - * 0 disables, 1 enables auto-ejecting. Useful to keep the tray in - * during shutdown. - * - * 2.8 Added first support (still BETA, I need feedback or a drive) for - * the Longshine LCS-7260 drives. They appear as double-speed drives - * using the "old" command scheme, extended by tray control and door - * lock functions. - * Found (and fixed preliminary) a flaw with some multisession CDs: we - * have to re-direct not only the accesses to frame 16 (the isofs - * routines drive it up to max. 100), but also those to the continuation - * (repetition) frames (as far as they exist - currently set fix as - * 16..20). - * Changed default of the "JUKEBOX" define. If you use this default, - * your tray will eject if you try to mount without a disk in. Next - * mount command will insert the tray - so, just fill in a disk. ;-) - * - * 2.9 Fulfilled the Longshine LCS-7260 support; with great help and - * experiments by Serge Robyns. - * First attempts to support the TEAC CD-55A drives; but still not - * usable yet. - * Implemented the CDROMMULTISESSION ioctl; this is an attempt to handle - * multi session CDs more "transparent" (redirection handling has to be - * done within the isofs routines, and only for the special purpose of - * obtaining the "right" volume descriptor; accesses to the raw device - * should not get redirected). - * - * 3.0 Just a "normal" increment, with some provisions to do it better. ;-) - * Introduced "#define READ_AUDIO" to specify the maximum number of - * audio frames to grab with one request. This defines a buffer size - * within kernel space; a value of 0 will reserve no such space and - * disable the CDROMREADAUDIO ioctl. A value of 75 enables the reading - * of a whole second with one command, but will use a buffer of more - * than 172 kB. - * Started CD200 support. Drive detection should work, but nothing - * more. - * - * 3.1 Working to support the CD200 and the Teac CD-55A drives. - * AT-BUS style device numbering no longer used: use SCSI style now. - * So, the first "found" device has MINOR 0, regardless of the - * jumpered drive ID. This implies modifications to the /dev/sbpcd* - * entries for some people, but will help the DAU (german TLA, english: - * "newbie", maybe ;-) to install his "first" system from a CD. - * - * 3.2 Still testing with CD200 and CD-55A drives. - * - * 3.3 Working with CD200 support. - * - * 3.4 Auto-probing stops if an address of 0 is seen (to be entered with - * the kernel command line). - * Made the driver "loadable". If used as a module, "audio copy" is - * disabled, and the internal read ahead data buffer has a reduced size - * of 4 kB; so, throughput may be reduced a little bit with slow CPUs. - * - * 3.5 Provisions to handle weird photoCDs which have an interrupted - * "formatting" immediately after the last frames of some files: simply - * never "read ahead" with MultiSession CDs. By this, CPU usage may be - * increased with those CDs, and there may be a loss in speed. - * Re-structured the messaging system. - * The "loadable" version no longer has a limited READ_AUDIO buffer - * size. - * Removed "MANY_SESSION" handling for "old" multi session CDs. - * Added "private" IOCTLs CDROMRESET and CDROMVOLREAD. - * Started again to support the TEAC CD-55A drives, now that I found - * the money for "my own" drive. ;-) - * The TEAC CD-55A support is fairly working now. - * I have measured that the drive "delivers" at 600 kB/sec (even with - * bigger requests than the drive's 64 kB buffer can satisfy), but - * the "real" rate does not exceed 520 kB/sec at the moment. - * Caused by the various changes to build in TEAC support, the timed - * loops are de-optimized at the moment (less throughput with CR-52x - * drives, and the TEAC will give speed only with SBP_BUFFER_FRAMES 64). - * - * 3.6 Fixed TEAC data read problems with SbPro interfaces. - * Initial size of the READ_AUDIO buffer is 0. Can get set to any size - * during runtime. - * - * 3.7 Introduced MAX_DRIVES for some poor interface cards (seen with TEAC - * drives) which allow only one drive (ID 0); this avoids repetitive - * detection under IDs 1..3. - * Elongated cmd_out_T response waiting; necessary for photo CDs with - * a lot of sessions. - * Bettered the sbpcd_open() behavior with TEAC drives. - * - * 3.8 Elongated max_latency for CR-56x drives. - * - * 3.9 Finally fixed the long-known SoundScape/SPEA/Sequoia S-1000 interface - * configuration bug. - * Now Corey, Heiko, Ken, Leo, Vadim/Eric & Werner are invited to copy - * the config_spea() routine into their drivers. ;-) - * - * 4.0 No "big step" - normal version increment. - * Adapted the benefits from 1.3.33. - * Fiddled with CDROMREADAUDIO flaws. - * Avoid ReadCapacity command with CD200 drives (the MKE 1.01 version - * seems not to support it). - * Fulfilled "read audio" for CD200 drives, with help of Pete Heist - * (heistp@rpi.edu). - * - * 4.1 Use loglevel KERN_INFO with printk(). - * Added support for "Vertos 100" drive ("ECS-AT") - it is very similar - * to the Longshine LCS-7260. Give feedback if you can - I never saw - * such a drive, and I have no specs. - * - * 4.2 Support for Teac 16-bit interface cards. Can't get auto-detected, - * so you have to jumper your card to 0x2C0. Still not 100% - come - * in contact if you can give qualified feedback. - * Use loglevel KERN_NOTICE with printk(). If you get annoyed by a - * flood of unwanted messages and the accompanied delay, try to read - * my documentation. Especially the Linux CDROM drivers have to do an - * important job for the newcomers, so the "distributed" version has - * to fit some special needs. Since generations, the flood of messages - * is user-configurable (even at runtime), but to get aware of this, one - * needs a special mental quality: the ability to read. - * - * 4.3 CD200F does not like to receive a command while the drive is - * reading the ToC; still trying to solve it. - * Removed some redundant verify_area calls (yes, Heiko Eissfeldt - * is visiting all the Linux CDROM drivers ;-). - * - * 4.4 Adapted one idea from tiensivu@pilot.msu.edu's "stripping-down" - * experiments: "KLOGD_PAUSE". - * Inhibited "play audio" attempts with data CDs. Provisions for a - * "data-safe" handling of "mixed" (data plus audio) Cds. - * - * 4.5 Meanwhile Gonzalo Tornaria <tornaria@cmat.edu.uy> (GTL) built a - * special end_request routine: we seem to have to take care for not - * to have two processes working at the request list. My understanding - * was and is that ll_rw_blk should not call do_sbpcd_request as long - * as there is still one call active (the first call will care for all - * outstanding I/Os, and if a second call happens, that is a bug in - * ll_rw_blk.c). - * "Check media change" without touching any drive. - * - * 4.6 Use a semaphore to synchronize multi-activity; elaborated by Rob - * Riggs <rriggs@tesser.com>. At the moment, we simply block "read" - * against "ioctl" and vice versa. This could be refined further, but - * I guess with almost no performance increase. - * Experiments to speed up the CD-55A; again with help of Rob Riggs - * (to be true, he gave both, idea & code. ;-) - * - * 4.61 Ported to Uniform CD-ROM driver by - * Heiko Eissfeldt <heiko@colossus.escape.de> with additional - * changes by Erik Andersen <andersee@debian.org> - * - * 4.62 Fix a bug where playing audio left the drive in an unusable state. - * Heiko Eissfeldt <heiko@colossus.escape.de> - * - * November 1999 -- Make kernel-parameter implementation work with 2.3.x - * Removed init_module & cleanup_module in favor of - * module_init & module_exit. - * Torben Mathiasen <tmm@image.dk> - * - * 4.63 Bug fixes for audio annoyances, new legacy CDROM maintainer. - * Annoying things fixed: - * TOC reread on automated disk changes - * TOC reread on manual cd changes - * Play IOCTL tries to play CD before it's actually ready... sometimes. - * CD_AUDIO_COMPLETED state so workman (and other playes) can repeat play. - * Andrew J. Kroll <ag784@freenet.buffalo.edu> Wed Jul 26 04:24:10 EDT 2000 - * - * 4.64 Fix module parameters - were being completely ignored. - * Can also specify max_drives=N as a setup int to get rid of - * "ghost" drives on crap hardware (aren't they all?) Paul Gortmaker - * - * TODO - * implement "read all subchannel data" (96 bytes per frame) - * remove alot of the virtual status bits and deal with hardware status - * move the change of cd for audio to a better place - * add debug levels to insmod parameters (trivial) - * - * special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine - * elaborated speed-up experiments (and the fabulous results!), for - * the "push" towards load-free wait loops, and for the extensive mail - * thread which brought additional hints and bug fixes. - * - */ - -/* - * Trying to merge requests breaks this driver horribly (as in it goes - * boom and apparently has done so since 2.3.41). As it is a legacy - * driver for a horribly slow double speed CD on a hideous interface - * designed for polled operation, I won't lose any sleep in simply - * disallowing merging. Paul G. 02/2001 - * - * Thu May 30 14:14:47 CEST 2002: - * - * I have presumably found the reson for the above - there was a bogous - * end_request substitute, which was manipulating the request queues - * incorrectly. If someone has access to the actual hardware, and it's - * still operations - well please free to test it. - * - * Marcin Dalecki - */ - -/* - * Add bio/kdev_t changes for 2.5.x required to make it work again. - * Still room for improvement in the request handling here if anyone - * actually cares. Bring your own chainsaw. Paul G. 02/2002 - */ - - -#include <linux/module.h> - -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/cdrom.h> -#include <linux/ioport.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/vmalloc.h> -#include <linux/init.h> -#include <linux/interrupt.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <stdarg.h> -#include "sbpcd.h" - -#define MAJOR_NR MATSUSHITA_CDROM_MAJOR -#include <linux/blkdev.h> - -/*==========================================================================*/ -#if SBPCD_DIS_IRQ -# define SBPCD_CLI cli() -# define SBPCD_STI sti() -#else -# define SBPCD_CLI -# define SBPCD_STI -#endif - -/*==========================================================================*/ -/* - * auto-probing address list - * inspired by Adam J. Richter from Yggdrasil - * - * still not good enough - can cause a hang. - * example: a NE 2000 ethernet card at 300 will cause a hang probing 310. - * if that happens, reboot and use the LILO (kernel) command line. - * The possibly conflicting ethernet card addresses get NOT probed - * by default - to minimize the hang possibilities. - * - * The SB Pro addresses get "mirrored" at 0x6xx and some more locations - to - * avoid a type error, the 0x2xx-addresses must get checked before 0x6xx. - * - * send mail to emoenke@gwdg.de if your interface card is not FULLY - * represented here. - */ -static int sbpcd[] = -{ - CDROM_PORT, SBPRO, /* probe with user's setup first */ -#if DISTRIBUTION - 0x230, 1, /* Soundblaster Pro and 16 (default) */ -#if 0 - 0x300, 0, /* CI-101P (default), WDH-7001C (default), - Galaxy (default), Reveal (one default) */ - 0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */ - 0x2C0, 3, /* Teac 16-bit cards */ - 0x260, 1, /* OmniCD */ - 0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default), - Longshine LCS-6853 (default) */ - 0x338, 0, /* Reveal Sound Wave 32 card model #SC600 */ - 0x340, 0, /* Mozart sound card (default), Lasermate, CI-101P */ - 0x360, 0, /* Lasermate, CI-101P */ - 0x270, 1, /* Soundblaster 16 */ - 0x670, 0, /* "sound card #9" */ - 0x690, 0, /* "sound card #9" */ - 0x338, 2, /* SPEA Media FX, Ensonic SoundScape (default) */ - 0x328, 2, /* SPEA Media FX */ - 0x348, 2, /* SPEA Media FX */ - 0x634, 0, /* some newer sound cards */ - 0x638, 0, /* some newer sound cards */ - 0x230, 1, /* some newer sound cards */ - /* due to incomplete address decoding of the SbPro card, these must be last */ - 0x630, 0, /* "sound card #9" (default) */ - 0x650, 0, /* "sound card #9" */ -#ifdef MODULE - /* - * some "hazardous" locations (no harm with the loadable version) - * (will stop the bus if a NE2000 ethernet card resides at offset -0x10) - */ - 0x330, 0, /* Lasermate, CI-101P, WDH-7001C */ - 0x350, 0, /* Lasermate, CI-101P */ - 0x358, 2, /* SPEA Media FX */ - 0x370, 0, /* Lasermate, CI-101P */ - 0x290, 1, /* Soundblaster 16 */ - 0x310, 0, /* Lasermate, CI-101P, WDH-7001C */ -#endif /* MODULE */ -#endif -#endif /* DISTRIBUTION */ -}; - -/* - * Protects access to global structures etc. - */ -static __cacheline_aligned DEFINE_SPINLOCK(sbpcd_lock); -static struct request_queue *sbpcd_queue; - -/* You can only set the first pair, from old MODULE_PARM code. */ -static int sbpcd_set(const char *val, struct kernel_param *kp) -{ - get_options((char *)val, 2, (int *)sbpcd); - return 0; -} -module_param_call(sbpcd, sbpcd_set, NULL, NULL, 0); - -#define NUM_PROBE (sizeof(sbpcd) / sizeof(int)) - -/*==========================================================================*/ - -#define INLINE inline - -/*==========================================================================*/ -/* - * the forward references: - */ -static void sbp_sleep(u_int); -static void mark_timeout_delay(u_long); -static void mark_timeout_data(u_long); -#if 0 -static void mark_timeout_audio(u_long); -#endif -static void sbp_read_cmd(struct request *req); -static int sbp_data(struct request *req); -static int cmd_out(void); -static int DiskInfo(void); - -/*==========================================================================*/ - -/* - * pattern for printk selection: - * - * (1<<DBG_INF) necessary information - * (1<<DBG_BSZ) BLOCK_SIZE trace - * (1<<DBG_REA) "read" status trace - * (1<<DBG_CHK) "media check" trace - * (1<<DBG_TIM) datarate timer test - * (1<<DBG_INI) initialization trace - * (1<<DBG_TOC) tell TocEntry values - * (1<<DBG_IOC) ioctl trace - * (1<<DBG_STA) "ResponseStatus" trace - * (1<<DBG_ERR) "cc_ReadError" trace - * (1<<DBG_CMD) "cmd_out" trace - * (1<<DBG_WRN) give explanation before auto-probing - * (1<<DBG_MUL) multi session code test - * (1<<DBG_IDX) "drive_id != 0" test code - * (1<<DBG_IOX) some special information - * (1<<DBG_DID) drive ID test - * (1<<DBG_RES) drive reset info - * (1<<DBG_SPI) SpinUp test info - * (1<<DBG_IOS) ioctl trace: "subchannel" - * (1<<DBG_IO2) ioctl trace: general - * (1<<DBG_UPC) show UPC info - * (1<<DBG_XA1) XA mode debugging - * (1<<DBG_LCK) door (un)lock info - * (1<<DBG_SQ1) dump SubQ frame - * (1<<DBG_AUD) "read audio" debugging - * (1<<DBG_SEQ) Sequoia interface configuration trace - * (1<<DBG_LCS) Longshine LCS-7260 debugging trace - * (1<<DBG_CD2) MKE/Funai CD200 debugging trace - * (1<<DBG_TEA) TEAC CD-55A debugging trace - * (1<<DBG_ECS) ECS-AT (Vertos-100) debugging trace - * (1<<DBG_000) unnecessary information - */ -#if DISTRIBUTION -static int sbpcd_debug = (1<<DBG_INF); -#else -static int sbpcd_debug = 0 & ((1<<DBG_INF) | - (1<<DBG_TOC) | - (1<<DBG_MUL) | - (1<<DBG_UPC)); -#endif /* DISTRIBUTION */ - -static int sbpcd_ioaddr = CDROM_PORT; /* default I/O base address */ -static int sbpro_type = SBPRO; -static unsigned char f_16bit; -static unsigned char do_16bit; -static int CDo_command, CDo_reset; -static int CDo_sel_i_d, CDo_enable; -static int CDi_info, CDi_status, CDi_data; -static struct cdrom_msf msf; -static struct cdrom_ti ti; -static struct cdrom_tochdr tochdr; -static struct cdrom_tocentry tocentry; -static struct cdrom_subchnl SC; -static struct cdrom_volctrl volctrl; -static struct cdrom_read_audio read_audio; - -static unsigned char msgnum; -static char msgbuf[80]; - -static int max_drives = MAX_DRIVES; -module_param(max_drives, int, 0); -#ifndef MODULE -static unsigned char setup_done; -static const char *str_sb_l = "soundblaster"; -static const char *str_sp_l = "spea"; -static const char *str_ss_l = "soundscape"; -static const char *str_t16_l = "teac16bit"; -static const char *str_ss = "SoundScape"; -#endif -static const char *str_sb = "SoundBlaster"; -static const char *str_lm = "LaserMate"; -static const char *str_sp = "SPEA"; -static const char *str_t16 = "Teac16bit"; -static const char *type; -static const char *major_name="sbpcd"; - -/*==========================================================================*/ - -#ifdef FUTURE -static DECLARE_WAIT_QUEUE_HEAD(sbp_waitq); -#endif /* FUTURE */ - -static int teac=SBP_TEAC_SPEED; -static int buffers=SBP_BUFFER_FRAMES; - -static u_char family0[]="MATSHITA"; /* MKE CR-521, CR-522, CR-523 */ -static u_char family1[]="CR-56"; /* MKE CR-562, CR-563 */ -static u_char family2[]="CD200"; /* MKE CD200, Funai CD200F */ -static u_char familyL[]="LCS-7260"; /* Longshine LCS-7260 */ -static u_char familyT[]="CD-55"; /* TEAC CD-55A */ -static u_char familyV[]="ECS-AT"; /* ECS Vertos 100 */ - -static u_int recursion; /* internal testing only */ -static u_int fatal_err; /* internal testing only */ -static u_int response_count; -static u_int flags_cmd_out; -static u_char cmd_type; -static u_char drvcmd[10]; -static u_char infobuf[20]; -static u_char xa_head_buf[CD_XA_HEAD]; -static u_char xa_tail_buf[CD_XA_TAIL]; - -#if OLD_BUSY -static volatile u_char busy_data; -static volatile u_char busy_audio; /* true semaphores would be safer */ -#endif /* OLD_BUSY */ -static DECLARE_MUTEX(ioctl_read_sem); -static u_long timeout; -static volatile u_char timed_out_delay; -static volatile u_char timed_out_data; -#if 0 -static volatile u_char timed_out_audio; -#endif -static u_int datarate= 1000000; -static u_int maxtim16=16000000; -static u_int maxtim04= 4000000; -static u_int maxtim02= 2000000; -static u_int maxtim_8= 30000; -#if LONG_TIMING -static u_int maxtim_data= 9000; -#else -static u_int maxtim_data= 3000; -#endif /* LONG_TIMING */ -#if DISTRIBUTION -static int n_retries=6; -#else -static int n_retries=6; -#endif -/*==========================================================================*/ - -static int ndrives; -static u_char drv_pattern[NR_SBPCD]={speed_auto,speed_auto,speed_auto,speed_auto}; - -/*==========================================================================*/ -/* - * drive space begins here (needed separate for each unit) - */ -static struct sbpcd_drive { - char drv_id; /* "jumpered" drive ID or -1 */ - char drv_sel; /* drive select lines bits */ - - char drive_model[9]; - u_char firmware_version[4]; - char f_eject; /* auto-eject flag: 0 or 1 */ - u_char *sbp_buf; /* Pointer to internal data buffer, - space allocated during sbpcd_init() */ - u_int sbp_bufsiz; /* size of sbp_buf (# of frames) */ - int sbp_first_frame; /* First frame in buffer */ - int sbp_last_frame; /* Last frame in buffer */ - int sbp_read_frames; /* Number of frames being read to buffer */ - int sbp_current; /* Frame being currently read */ - - u_char mode; /* read_mode: READ_M1, READ_M2, READ_SC, READ_AU */ - u_char *aud_buf; /* Pointer to audio data buffer, - space allocated during sbpcd_init() */ - u_int sbp_audsiz; /* size of aud_buf (# of raw frames) */ - u_int drv_type; - u_char drv_options; - int status_bits; - u_char diskstate_flags; - u_char sense_byte; - - u_char CD_changed; - char open_count; - u_char error_byte; - - u_char f_multisession; - u_int lba_multi; - int first_session; - int last_session; - int track_of_last_session; - - u_char audio_state; - u_int pos_audio_start; - u_int pos_audio_end; - char vol_chan0; - u_char vol_ctrl0; - char vol_chan1; - u_char vol_ctrl1; -#if 000 /* no supported drive has it */ - char vol_chan2; - u_char vol_ctrl2; - char vol_chan3; - u_char vol_ctrl3; -#endif /*000 */ - u_char volume_control; /* TEAC on/off bits */ - - u_char SubQ_ctl_adr; - u_char SubQ_trk; - u_char SubQ_pnt_idx; - u_int SubQ_run_tot; - u_int SubQ_run_trk; - u_char SubQ_whatisthis; - - u_char UPC_ctl_adr; - u_char UPC_buf[7]; - - int frame_size; - int CDsize_frm; - - u_char xa_byte; /* 0x20: XA capabilities */ - u_char n_first_track; /* binary */ - u_char n_last_track; /* binary (not bcd), 0x01...0x63 */ - u_int size_msf; /* time of whole CD, position of LeadOut track */ - u_int size_blk; - - u_char TocEnt_nixbyte; /* em */ - u_char TocEnt_ctl_adr; - u_char TocEnt_number; - u_char TocEnt_format; /* em */ - u_int TocEnt_address; -#ifdef SAFE_MIXED - char has_data; -#endif /* SAFE_MIXED */ - u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */ - - struct { - u_char nixbyte; /* em */ - u_char ctl_adr; /* 0x4x: data, 0x0x: audio */ - u_char number; - u_char format; /* em */ /* 0x00: lba, 0x01: msf */ - u_int address; - } TocBuffer[MAX_TRACKS+1]; /* last entry faked */ - - int in_SpinUp; /* CR-52x test flag */ - int n_bytes; /* TEAC awaited response count */ - u_char error_state, b3, b4; /* TEAC command error state */ - u_char f_drv_error; /* TEAC command error flag */ - u_char speed_byte; - int frmsiz; - u_char f_XA; /* 1: XA */ - u_char type_byte; /* 0, 1, 3 */ - u_char mode_xb_6; - u_char mode_yb_7; - u_char mode_xb_8; - u_char delay; - struct cdrom_device_info *sbpcd_infop; - struct gendisk *disk; -} D_S[NR_SBPCD]; - -static struct sbpcd_drive *current_drive = D_S; - -/* - * drive space ends here (needed separate for each unit) - */ -/*==========================================================================*/ -#if 0 -unsigned long cli_sti; /* for saving the processor flags */ -#endif -/*==========================================================================*/ -static DEFINE_TIMER(delay_timer, mark_timeout_delay, 0, 0); -static DEFINE_TIMER(data_timer, mark_timeout_data, 0, 0); -#if 0 -static DEFINE_TIMER(audio_timer, mark_timeout_audio, 0, 0); -#endif -/*==========================================================================*/ -/* - * DDI interface - */ -static void msg(int level, const char *fmt, ...) -{ -#if DISTRIBUTION -#define MSG_LEVEL KERN_NOTICE -#else -#define MSG_LEVEL KERN_INFO -#endif /* DISTRIBUTION */ - - char buf[256]; - va_list args; - - if (!(sbpcd_debug&(1<<level))) return; - - msgnum++; - if (msgnum>99) msgnum=0; - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - printk(MSG_LEVEL "%s-%d [%02d]: %s", major_name, current_drive - D_S, msgnum, buf); -#if KLOGD_PAUSE - sbp_sleep(KLOGD_PAUSE); /* else messages get lost */ -#endif /* KLOGD_PAUSE */ - return; -} -/*==========================================================================*/ -/* - * DDI interface: runtime trace bit pattern maintenance - */ -static int sbpcd_dbg_ioctl(unsigned long arg, int level) -{ - switch(arg) - { - case 0: /* OFF */ - sbpcd_debug = DBG_INF; - break; - - default: - if (arg>=128) sbpcd_debug &= ~(1<<(arg-128)); - else sbpcd_debug |= (1<<arg); - } - return (arg); -} -/*==========================================================================*/ -static void mark_timeout_delay(u_long i) -{ - timed_out_delay=1; -#if 0 - msg(DBG_TIM,"delay timer expired.\n"); -#endif -} -/*==========================================================================*/ -static void mark_timeout_data(u_long i) -{ - timed_out_data=1; -#if 0 - msg(DBG_TIM,"data timer expired.\n"); -#endif -} -/*==========================================================================*/ -#if 0 -static void mark_timeout_audio(u_long i) -{ - timed_out_audio=1; -#if 0 - msg(DBG_TIM,"audio timer expired.\n"); -#endif -} -#endif -/*==========================================================================*/ -/* - * Wait a little while (used for polling the drive). - */ -static void sbp_sleep(u_int time) -{ - sti(); - schedule_timeout_interruptible(time); - sti(); -} -/*==========================================================================*/ -#define RETURN_UP(rc) {up(&ioctl_read_sem); return(rc);} -/*==========================================================================*/ -/* - * convert logical_block_address to m-s-f_number (3 bytes only) - */ -static INLINE void lba2msf(int lba, u_char *msf) -{ - lba += CD_MSF_OFFSET; - msf[0] = lba / (CD_SECS*CD_FRAMES); - lba %= CD_SECS*CD_FRAMES; - msf[1] = lba / CD_FRAMES; - msf[2] = lba % CD_FRAMES; -} -/*==========================================================================*/ -/*==========================================================================*/ -/* - * convert msf-bin to msf-bcd - */ -static INLINE void bin2bcdx(u_char *p) /* must work only up to 75 or 99 */ -{ - *p=((*p/10)<<4)|(*p%10); -} -/*==========================================================================*/ -static INLINE u_int blk2msf(u_int blk) -{ - MSF msf; - u_int mm; - - msf.c[3] = 0; - msf.c[2] = (blk + CD_MSF_OFFSET) / (CD_SECS * CD_FRAMES); - mm = (blk + CD_MSF_OFFSET) % (CD_SECS * CD_FRAMES); - msf.c[1] = mm / CD_FRAMES; - msf.c[0] = mm % CD_FRAMES; - return (msf.n); -} -/*==========================================================================*/ -static INLINE u_int make16(u_char rh, u_char rl) -{ - return ((rh<<8)|rl); -} -/*==========================================================================*/ -static INLINE u_int make32(u_int rh, u_int rl) -{ - return ((rh<<16)|rl); -} -/*==========================================================================*/ -static INLINE u_char swap_nibbles(u_char i) -{ - return ((i<<4)|(i>>4)); -} -/*==========================================================================*/ -static INLINE u_char byt2bcd(u_char i) -{ - return (((i/10)<<4)+i%10); -} -/*==========================================================================*/ -static INLINE u_char bcd2bin(u_char bcd) -{ - return ((bcd>>4)*10+(bcd&0x0F)); -} -/*==========================================================================*/ -static INLINE int msf2blk(int msfx) -{ - MSF msf; - int i; - - msf.n=msfx; - i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_MSF_OFFSET; - if (i<0) return (0); - return (i); -} -/*==========================================================================*/ -/* - * convert m-s-f_number (3 bytes only) to logical_block_address - */ -static INLINE int msf2lba(u_char *msf) -{ - int i; - - i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_MSF_OFFSET; - if (i<0) return (0); - return (i); -} -/*==========================================================================*/ -/* evaluate cc_ReadError code */ -static int sta2err(int sta) -{ - if (famT_drive) - { - if (sta==0x00) return (0); - if (sta==0x01) return (-604); /* CRC error */ - if (sta==0x02) return (-602); /* drive not ready */ - if (sta==0x03) return (-607); /* unknown media */ - if (sta==0x04) return (-612); /* general failure */ - if (sta==0x05) return (0); - if (sta==0x06) return (-ERR_DISKCHANGE); /* disk change */ - if (sta==0x0b) return (-612); /* general failure */ - if (sta==0xff) return (-612); /* general failure */ - return (0); - } - else - { - if (sta<=2) return (sta); - if (sta==0x05) return (-604); /* CRC error */ - if (sta==0x06) return (-606); /* seek error */ - if (sta==0x0d) return (-606); /* seek error */ - if (sta==0x0e) return (-603); /* unknown command */ - if (sta==0x14) return (-603); /* unknown command */ - if (sta==0x0c) return (-611); /* read fault */ - if (sta==0x0f) return (-611); /* read fault */ - if (sta==0x10) return (-611); /* read fault */ - if (sta>=0x16) return (-612); /* general failure */ - if (sta==0x11) return (-ERR_DISKCHANGE); /* disk change (LCS: removed) */ - if (famL_drive) - if (sta==0x12) return (-ERR_DISKCHANGE); /* disk change (inserted) */ - return (-602); /* drive not ready */ - } -} -/*==========================================================================*/ -static INLINE void clr_cmdbuf(void) -{ - int i; - - for (i=0;i<10;i++) drvcmd[i]=0; - cmd_type=0; -} -/*==========================================================================*/ -static void flush_status(void) -{ - int i; - - sbp_sleep(15*HZ/10); - for (i=maxtim_data;i!=0;i--) inb(CDi_status); -} -/*====================================================================*/ -/* - * CDi status loop for Teac CD-55A (Rob Riggs) - * - * This is needed because for some strange reason - * the CD-55A can take a real long time to give a - * status response. This seems to happen after we - * issue a READ command where a long seek is involved. - * - * I tried to ensure that we get max throughput with - * minimal busy waiting. We busy wait at first, then - * "switch gears" and start sleeping. We sleep for - * longer periods of time the longer we wait. - * - */ -static int CDi_stat_loop_T(void) -{ - int i, gear=1; - u_long timeout_1, timeout_2, timeout_3, timeout_4; - - timeout_1 = jiffies + HZ / 50; /* sbp_sleep(0) for a short period */ - timeout_2 = jiffies + HZ / 5; /* nap for no more than 200ms */ - timeout_3 = jiffies + 5 * HZ; /* sleep for up to 5s */ - timeout_4 = jiffies + 45 * HZ; /* long sleep for up to 45s. */ - do - { - i = inb(CDi_status); - if (!(i&s_not_data_ready)) return (i); - if (!(i&s_not_result_ready)) return (i); - switch(gear) - { - case 4: - sbp_sleep(HZ); - if (time_after(jiffies, timeout_4)) gear++; - msg(DBG_TEA, "CDi_stat_loop_T: long sleep active.\n"); - break; - case 3: - sbp_sleep(HZ/10); - if (time_after(jiffies, timeout_3)) gear++; - break; - case 2: - sbp_sleep(HZ/100); - if (time_after(jiffies, timeout_2)) gear++; - break; - case 1: - sbp_sleep(0); - if (time_after(jiffies, timeout_1)) gear++; - } - } while (gear < 5); - return -1; -} -/*==========================================================================*/ -static int CDi_stat_loop(void) -{ - int i,j; - - for(timeout = jiffies + 10*HZ, i=maxtim_data; time_before(jiffies, timeout); ) - { - for ( ;i!=0;i--) - { - j=inb(CDi_status); - if (!(j&s_not_data_ready)) return (j); - if (!(j&s_not_result_ready)) return (j); - if (fam0L_drive) if (j&s_attention) return (j); - } - sbp_sleep(1); - i = 1; - } - msg(DBG_LCS,"CDi_stat_loop failed in line %d\n", __LINE__); - return (-1); -} -/*==========================================================================*/ -#if 00000 -/*==========================================================================*/ -static int tst_DataReady(void) -{ - int i; - - i=inb(CDi_status); - if (i&s_not_data_ready) return (0); - return (1); -} -/*==========================================================================*/ -static int tst_ResultReady(void) -{ - int i; - - i=inb(CDi_status); - if (i&s_not_result_ready) return (0); - return (1); -} -/*==========================================================================*/ -static int tst_Attention(void) -{ - int i; - - i=inb(CDi_status); - if (i&s_attention) return (1); - return (0); -} -/*==========================================================================*/ -#endif -/*==========================================================================*/ -static int ResponseInfo(void) -{ - int i,j,st=0; - u_long timeout; - - for (i=0,timeout=jiffies+HZ;i<response_count;i++) - { - for (j=maxtim_data; ; ) - { - for ( ;j!=0;j-- ) - { - st=inb(CDi_status); - if (!(st&s_not_result_ready)) break; - } - if ((j!=0)||time_after_eq(jiffies, timeout)) break; - sbp_sleep(1); - j = 1; - } - if (time_after_eq(jiffies, timeout)) break; - infobuf[i]=inb(CDi_info); - } -#if 000 - while (!(inb(CDi_status)&s_not_result_ready)) - { - infobuf[i++]=inb(CDi_info); - } - j=i-response_count; - if (j>0) msg(DBG_INF,"ResponseInfo: got %d trailing bytes.\n",j); -#endif /* 000 */ - for (j=0;j<i;j++) - sprintf(&msgbuf[j*3]," %02X",infobuf[j]); - msgbuf[j*3]=0; - msg(DBG_CMD,"ResponseInfo:%s (%d,%d)\n",msgbuf,response_count,i); - j=response_count-i; - if (j>0) return (-j); - else return (i); -} -/*==========================================================================*/ -static void EvaluateStatus(int st) -{ - current_drive->status_bits=0; - if (fam1_drive) current_drive->status_bits=st|p_success; - else if (fam0_drive) - { - if (st&p_caddin_old) current_drive->status_bits |= p_door_closed|p_caddy_in; - if (st&p_spinning) current_drive->status_bits |= p_spinning; - if (st&p_check) current_drive->status_bits |= p_check; - if (st&p_success_old) current_drive->status_bits |= p_success; - if (st&p_busy_old) current_drive->status_bits |= p_busy_new; - if (st&p_disk_ok) current_drive->status_bits |= p_disk_ok; - } - else if (famLV_drive) - { - current_drive->status_bits |= p_success; - if (st&p_caddin_old) current_drive->status_bits |= p_disk_ok|p_caddy_in; - if (st&p_spinning) current_drive->status_bits |= p_spinning; - if (st&p_check) current_drive->status_bits |= p_check; - if (st&p_busy_old) current_drive->status_bits |= p_busy_new; - if (st&p_lcs_door_closed) current_drive->status_bits |= p_door_closed; - if (st&p_lcs_door_locked) current_drive->status_bits |= p_door_locked; - } - else if (fam2_drive) - { - current_drive->status_bits |= p_success; - if (st&p2_check) current_drive->status_bits |= p1_check; - if (st&p2_door_closed) current_drive->status_bits |= p1_door_closed; - if (st&p2_disk_in) current_drive->status_bits |= p1_disk_in; - if (st&p2_busy1) current_drive->status_bits |= p1_busy; - if (st&p2_busy2) current_drive->status_bits |= p1_busy; - if (st&p2_spinning) current_drive->status_bits |= p1_spinning; - if (st&p2_door_locked) current_drive->status_bits |= p1_door_locked; - if (st&p2_disk_ok) current_drive->status_bits |= p1_disk_ok; - } - else if (famT_drive) - { - return; /* still needs to get coded */ - current_drive->status_bits |= p_success; - if (st&p2_check) current_drive->status_bits |= p1_check; - if (st&p2_door_closed) current_drive->status_bits |= p1_door_closed; - if (st&p2_disk_in) current_drive->status_bits |= p1_disk_in; - if (st&p2_busy1) current_drive->status_bits |= p1_busy; - if (st&p2_busy2) current_drive->status_bits |= p1_busy; - if (st&p2_spinning) current_drive->status_bits |= p1_spinning; - if (st&p2_door_locked) current_drive->status_bits |= p1_door_locked; - if (st&p2_disk_ok) current_drive->status_bits |= p1_disk_ok; - } - return; -} -/*==========================================================================*/ -static int cmd_out_T(void); - -static int get_state_T(void) -{ - int i; - - clr_cmdbuf(); - current_drive->n_bytes=1; - drvcmd[0]=CMDT_STATUS; - i=cmd_out_T(); - if (i>=0) i=infobuf[0]; - else - { - msg(DBG_TEA,"get_state_T error %d\n", i); - return (i); - } - if (i>=0) - /* 2: closed, disk in */ - current_drive->status_bits=p1_door_closed|p1_disk_in|p1_spinning|p1_disk_ok; - else if (current_drive->error_state==6) - { - /* 3: closed, disk in, changed ("06 xx xx") */ - current_drive->status_bits=p1_door_closed|p1_disk_in; - current_drive->CD_changed=0xFF; - current_drive->diskstate_flags &= ~toc_bit; - } - else if ((current_drive->error_state!=2)||(current_drive->b3!=0x3A)||(current_drive->b4==0x00)) - { - /* 1: closed, no disk ("xx yy zz"or "02 3A 00") */ - current_drive->status_bits=p1_door_closed; - current_drive->open_count=0; - } - else if (current_drive->b4==0x01) - { - /* 0: open ("02 3A 01") */ - current_drive->status_bits=0; - current_drive->open_count=0; - } - else - { - /* 1: closed, no disk ("02 3A xx") */ - current_drive->status_bits=p1_door_closed; - current_drive->open_count=0; - } - return (current_drive->status_bits); -} -/*==========================================================================*/ -static int ResponseStatus(void) -{ - int i,j; - u_long timeout; - - msg(DBG_STA,"doing ResponseStatus...\n"); - if (famT_drive) return (get_state_T()); - if (flags_cmd_out & f_respo3) timeout = jiffies; - else if (flags_cmd_out & f_respo2) timeout = jiffies + 16*HZ; - else timeout = jiffies + 4*HZ; - j=maxtim_8; - do - { - for ( ;j!=0;j--) - { - i=inb(CDi_status); - if (!(i&s_not_result_ready)) break; - } - if ((j!=0)||time_after(jiffies, timeout)) break; - sbp_sleep(1); - j = 1; - } - while (1); - if (j==0) - { - if ((flags_cmd_out & f_respo3) == 0) - msg(DBG_STA,"ResponseStatus: timeout.\n"); - current_drive->status_bits=0; - return (-401); - } - i=inb(CDi_info); - msg(DBG_STA,"ResponseStatus: response %02X.\n", i); - EvaluateStatus(i); - msg(DBG_STA,"status_bits=%02X, i=%02X\n",current_drive->status_bits,i); - return (current_drive->status_bits); -} -/*==========================================================================*/ -static void cc_ReadStatus(void) -{ - int i; - - msg(DBG_STA,"giving cc_ReadStatus command\n"); - if (famT_drive) return; - SBPCD_CLI; - if (fam0LV_drive) OUT(CDo_command,CMD0_STATUS); - else if (fam1_drive) OUT(CDo_command,CMD1_STATUS); - else if (fam2_drive) OUT(CDo_command,CMD2_STATUS); - if (!fam0LV_drive) for (i=0;i<6;i++) OUT(CDo_command,0); - SBPCD_STI; -} -/*==========================================================================*/ -static int cc_ReadError(void) -{ - int i; - - clr_cmdbuf(); - msg(DBG_ERR,"giving cc_ReadError command.\n"); - if (fam1_drive) - { - drvcmd[0]=CMD1_READ_ERR; - response_count=8; - flags_cmd_out=f_putcmd|f_ResponseStatus; - } - else if (fam0LV_drive) - { - drvcmd[0]=CMD0_READ_ERR; - response_count=6; - if (famLV_drive) - flags_cmd_out=f_putcmd; - else - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_READ_ERR; - response_count=6; - flags_cmd_out=f_putcmd; - } - else if (famT_drive) - { - response_count=5; - drvcmd[0]=CMDT_READ_ERR; - } - i=cmd_out(); - current_drive->error_byte=0; - msg(DBG_ERR,"cc_ReadError: cmd_out(CMDx_READ_ERR) returns %d (%02X)\n",i,i); - if (i<0) return (i); - if (fam0V_drive) i=1; - else i=2; - current_drive->error_byte=infobuf[i]; - msg(DBG_ERR,"cc_ReadError: infobuf[%d] is %d (%02X)\n",i,current_drive->error_byte,current_drive->error_byte); - i=sta2err(infobuf[i]); - if (i==-ERR_DISKCHANGE) - { - current_drive->CD_changed=0xFF; - current_drive->diskstate_flags &= ~toc_bit; - } - return (i); -} -/*==========================================================================*/ -static int cc_DriveReset(void); - -static int cmd_out_T(void) -{ -#undef CMDT_TRIES -#define CMDT_TRIES 1000 -#define TEST_FALSE_FF 1 - - int i, j, l=0, m, ntries; - unsigned long flags; - - current_drive->error_state=0; - current_drive->b3=0; - current_drive->b4=0; - current_drive->f_drv_error=0; - for (i=0;i<10;i++) sprintf(&msgbuf[i*3]," %02X",drvcmd[i]); - msgbuf[i*3]=0; - msg(DBG_CMD,"cmd_out_T:%s\n",msgbuf); - - OUT(CDo_sel_i_d,0); - OUT(CDo_enable,current_drive->drv_sel); - i=inb(CDi_status); - do_16bit=0; - if ((f_16bit)&&(!(i&0x80))) - { - do_16bit=1; - msg(DBG_TEA,"cmd_out_T: do_16bit set.\n"); - } - if (!(i&s_not_result_ready)) - do - { - j=inb(CDi_info); - i=inb(CDi_status); - sbp_sleep(0); - msg(DBG_TEA,"cmd_out_T: spurious !s_not_result_ready. (%02X)\n", j); - } - while (!(i&s_not_result_ready)); - save_flags(flags); cli(); - for (i=0;i<10;i++) OUT(CDo_command,drvcmd[i]); - restore_flags(flags); - for (ntries=CMDT_TRIES;ntries>0;ntries--) - { - if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(HZ); /* fixme */ -#if 01 - OUT(CDo_sel_i_d,1); -#endif /* 01 */ - if (teac==2) - { - if ((i=CDi_stat_loop_T()) == -1) break; - } - else - { -#if 0 - OUT(CDo_sel_i_d,1); -#endif /* 0 */ - i=inb(CDi_status); - } - if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */ - { - OUT(CDo_sel_i_d,1); - if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */ - if (drvcmd[0]==CMDT_DISKINFO) - { - l=0; - do - { - if (do_16bit) - { - i=inw(CDi_data); - infobuf[l++]=i&0x0ff; - infobuf[l++]=i>>8; -#if TEST_FALSE_FF - if ((l==2)&&(infobuf[0]==0x0ff)) - { - infobuf[0]=infobuf[1]; - l=1; - msg(DBG_TEA,"cmd_out_T: do_16bit: false first byte!\n"); - } -#endif /* TEST_FALSE_FF */ - } - else infobuf[l++]=inb(CDi_data); - i=inb(CDi_status); - } - while (!(i&s_not_data_ready)); - for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]); - msgbuf[j*3]=0; - msg(DBG_CMD,"cmd_out_T data response:%s\n", msgbuf); - } - else - { - msg(DBG_TEA,"cmd_out_T: data response with cmd_%02X!\n", - drvcmd[0]); - j=0; - do - { - if (do_16bit) i=inw(CDi_data); - else i=inb(CDi_data); - j++; - i=inb(CDi_status); - } - while (!(i&s_not_data_ready)); - msg(DBG_TEA,"cmd_out_T: data response: discarded %d bytes/words.\n", j); - fatal_err++; - } - } - i=inb(CDi_status); - if (!(i&s_not_result_ready)) - { - OUT(CDo_sel_i_d,0); - if (drvcmd[0]==CMDT_DISKINFO) m=l; - else m=0; - do - { - infobuf[m++]=inb(CDi_info); - i=inb(CDi_status); - } - while (!(i&s_not_result_ready)); - for (j=0;j<m;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]); - msgbuf[j*3]=0; - msg(DBG_CMD,"cmd_out_T info response:%s\n", msgbuf); - if (drvcmd[0]==CMDT_DISKINFO) - { - infobuf[0]=infobuf[l]; - if (infobuf[0]!=0x02) return (l); /* data length */ - } - else if (infobuf[0]!=0x02) return (m); /* info length */ - do - { - ++recursion; - if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (%02X): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", drvcmd[0], recursion); - clr_cmdbuf(); - drvcmd[0]=CMDT_READ_ERR; - j=cmd_out_T(); /* !!! recursive here !!! */ - --recursion; - sbp_sleep(1); - } - while (j<0); - current_drive->error_state=infobuf[2]; - current_drive->b3=infobuf[3]; - current_drive->b4=infobuf[4]; - if (current_drive->f_drv_error) - { - current_drive->f_drv_error=0; - cc_DriveReset(); - current_drive->error_state=2; - } - return (-current_drive->error_state-400); - } - if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */ - if ((teac==0)||(ntries<(CMDT_TRIES-5))) sbp_sleep(HZ/10); - else sbp_sleep(HZ/100); - if (ntries>(CMDT_TRIES-50)) continue; - msg(DBG_TEA,"cmd_out_T: next CMDT_TRIES (%02X): %d.\n", drvcmd[0], ntries-1); - } - current_drive->f_drv_error=1; - cc_DriveReset(); - current_drive->error_state=2; - return (-99); -} -/*==========================================================================*/ -static int cmd_out(void) -{ - int i=0; - - if (famT_drive) return(cmd_out_T()); - - if (flags_cmd_out&f_putcmd) - { - unsigned long flags; - for (i=0;i<7;i++) - sprintf(&msgbuf[i*3], " %02X", drvcmd[i]); - msgbuf[i*3]=0; - msg(DBG_CMD,"cmd_out:%s\n", msgbuf); - save_flags(flags); cli(); - for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]); - restore_flags(flags); - } - if (response_count!=0) - { - if (cmd_type!=0) - { - if (sbpro_type==1) OUT(CDo_sel_i_d,1); - msg(DBG_INF,"misleaded to try ResponseData.\n"); - if (sbpro_type==1) OUT(CDo_sel_i_d,0); - return (-22); - } - else i=ResponseInfo(); - if (i<0) return (i); - } - if (current_drive->in_SpinUp) msg(DBG_SPI,"in_SpinUp: to CDi_stat_loop.\n"); - if (flags_cmd_out&f_lopsta) - { - i=CDi_stat_loop(); - if ((i<0)||!(i&s_attention)) return (-8); - } - if (!(flags_cmd_out&f_getsta)) goto LOC_229; - - LOC_228: - if (current_drive->in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadStatus.\n"); - cc_ReadStatus(); - - LOC_229: - if (flags_cmd_out&f_ResponseStatus) - { - if (current_drive->in_SpinUp) msg(DBG_SPI,"in_SpinUp: to ResponseStatus.\n"); - i=ResponseStatus(); - /* builds status_bits, returns orig. status or p_busy_new */ - if (i<0) return (i); - if (flags_cmd_out&(f_bit1|f_wait_if_busy)) - { - if (!st_check) - { - if ((flags_cmd_out&f_bit1)&&(i&p_success)) goto LOC_232; - if ((!(flags_cmd_out&f_wait_if_busy))||(!st_busy)) goto LOC_228; - } - } - } - LOC_232: - if (!(flags_cmd_out&f_obey_p_check)) return (0); - if (!st_check) return (0); - if (current_drive->in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadError.\n"); - i=cc_ReadError(); - if (current_drive->in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cmd_out OK.\n"); - msg(DBG_000,"cmd_out: cc_ReadError=%d\n", i); - return (i); -} -/*==========================================================================*/ -static int cc_Seek(u_int pos, char f_blk_msf) -{ - int i; - - clr_cmdbuf(); - if (f_blk_msf>1) return (-3); - if (fam0V_drive) - { - drvcmd[0]=CMD0_SEEK; - if (f_blk_msf==1) pos=msf2blk(pos); - drvcmd[2]=(pos>>16)&0x00FF; - drvcmd[3]=(pos>>8)&0x00FF; - drvcmd[4]=pos&0x00FF; - if (fam0_drive) - flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta | - f_ResponseStatus | f_obey_p_check | f_bit1; - else - flags_cmd_out = f_putcmd; - } - else if (fam1L_drive) - { - drvcmd[0]=CMD1_SEEK; /* same as CMD1_ and CMDL_ */ - if (f_blk_msf==0) pos=blk2msf(pos); - drvcmd[1]=(pos>>16)&0x00FF; - drvcmd[2]=(pos>>8)&0x00FF; - drvcmd[3]=pos&0x00FF; - if (famL_drive) - flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; - else - flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_SEEK; - if (f_blk_msf==0) pos=blk2msf(pos); - drvcmd[2]=(pos>>24)&0x00FF; - drvcmd[3]=(pos>>16)&0x00FF; - drvcmd[4]=(pos>>8)&0x00FF; - drvcmd[5]=pos&0x00FF; - flags_cmd_out=f_putcmd|f_ResponseStatus; - } - else if (famT_drive) - { - drvcmd[0]=CMDT_SEEK; - if (f_blk_msf==1) pos=msf2blk(pos); - drvcmd[2]=(pos>>24)&0x00FF; - drvcmd[3]=(pos>>16)&0x00FF; - drvcmd[4]=(pos>>8)&0x00FF; - drvcmd[5]=pos&0x00FF; - current_drive->n_bytes=1; - } - response_count=0; - i=cmd_out(); - return (i); -} -/*==========================================================================*/ -static int cc_SpinUp(void) -{ - int i; - - msg(DBG_SPI,"SpinUp.\n"); - current_drive->in_SpinUp = 1; - clr_cmdbuf(); - if (fam0LV_drive) - { - drvcmd[0]=CMD0_SPINUP; - if (fam0L_drive) - flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta| - f_ResponseStatus|f_obey_p_check|f_bit1; - else - flags_cmd_out=f_putcmd; - } - else if (fam1_drive) - { - drvcmd[0]=CMD1_SPINUP; - flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_TRAY_CTL; - drvcmd[4]=0x01; /* "spinup" */ - flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; - } - else if (famT_drive) - { - drvcmd[0]=CMDT_TRAY_CTL; - drvcmd[4]=0x03; /* "insert", it hopefully spins the drive up */ - } - response_count=0; - i=cmd_out(); - current_drive->in_SpinUp = 0; - return (i); -} -/*==========================================================================*/ -static int cc_SpinDown(void) -{ - int i; - - if (fam0_drive) return (0); - clr_cmdbuf(); - response_count=0; - if (fam1_drive) - { - drvcmd[0]=CMD1_SPINDOWN; - flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_TRAY_CTL; - drvcmd[4]=0x02; /* "eject" */ - flags_cmd_out=f_putcmd|f_ResponseStatus; - } - else if (famL_drive) - { - drvcmd[0]=CMDL_SPINDOWN; - drvcmd[1]=1; - flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; - } - else if (famV_drive) - { - drvcmd[0]=CMDV_SPINDOWN; - flags_cmd_out=f_putcmd; - } - else if (famT_drive) - { - drvcmd[0]=CMDT_TRAY_CTL; - drvcmd[4]=0x02; /* "eject" */ - } - i=cmd_out(); - return (i); -} -/*==========================================================================*/ -static int cc_get_mode_T(void) -{ - int i; - - clr_cmdbuf(); - response_count=10; - drvcmd[0]=CMDT_GETMODE; - drvcmd[4]=response_count; - i=cmd_out_T(); - return (i); -} -/*==========================================================================*/ -static int cc_set_mode_T(void) -{ - int i; - - clr_cmdbuf(); - response_count=1; - drvcmd[0]=CMDT_SETMODE; - drvcmd[1]=current_drive->speed_byte; - drvcmd[2]=current_drive->frmsiz>>8; - drvcmd[3]=current_drive->frmsiz&0x0FF; - drvcmd[4]=current_drive->f_XA; /* 1: XA */ - drvcmd[5]=current_drive->type_byte; /* 0, 1, 3 */ - drvcmd[6]=current_drive->mode_xb_6; - drvcmd[7]=current_drive->mode_yb_7|current_drive->volume_control; - drvcmd[8]=current_drive->mode_xb_8; - drvcmd[9]=current_drive->delay; - i=cmd_out_T(); - return (i); -} -/*==========================================================================*/ -static int cc_prep_mode_T(void) -{ - int i, j; - - i=cc_get_mode_T(); - if (i<0) return (i); - for (i=0;i<10;i++) - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf); - current_drive->speed_byte=0x02; /* 0x02: auto quad, 0x82: quad, 0x81: double, 0x80: single */ - current_drive->frmsiz=make16(infobuf[2],infobuf[3]); - current_drive->f_XA=infobuf[4]; - if (current_drive->f_XA==0) current_drive->type_byte=0; - else current_drive->type_byte=1; - current_drive->mode_xb_6=infobuf[6]; - current_drive->mode_yb_7=1; - current_drive->mode_xb_8=infobuf[8]; - current_drive->delay=0; /* 0, 1, 2, 3 */ - j=cc_set_mode_T(); - i=cc_get_mode_T(); - for (i=0;i<10;i++) - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf); - return (j); -} -/*==========================================================================*/ -static int cc_SetSpeed(u_char speed, u_char x1, u_char x2) -{ - int i; - - if (fam0LV_drive) return (0); - clr_cmdbuf(); - response_count=0; - if (fam1_drive) - { - drvcmd[0]=CMD1_SETMODE; - drvcmd[1]=0x03; - drvcmd[2]=speed; - drvcmd[3]=x1; - drvcmd[4]=x2; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_SETSPEED; - if (speed&speed_auto) - { - drvcmd[2]=0xFF; - drvcmd[3]=0xFF; - } - else - { - drvcmd[2]=0; - drvcmd[3]=150; - } - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (famT_drive) - { - return (0); - } - i=cmd_out(); - return (i); -} -/*==========================================================================*/ -static int cc_SetVolume(void) -{ - int i; - u_char channel0,channel1,volume0,volume1; - u_char control0,value0,control1,value1; - - current_drive->diskstate_flags &= ~volume_bit; - clr_cmdbuf(); - channel0=current_drive->vol_chan0; - volume0=current_drive->vol_ctrl0; - channel1=control1=current_drive->vol_chan1; - volume1=value1=current_drive->vol_ctrl1; - control0=value0=0; - - if (famV_drive) return (0); - - if (((current_drive->drv_options&audio_mono)!=0)&&(current_drive->drv_type>=drv_211)) - { - if ((volume0!=0)&&(volume1==0)) - { - volume1=volume0; - channel1=channel0; - } - else if ((volume0==0)&&(volume1!=0)) - { - volume0=volume1; - channel0=channel1; - } - } - if (channel0>1) - { - channel0=0; - volume0=0; - } - if (channel1>1) - { - channel1=1; - volume1=0; - } - - if (fam1_drive) - { - control0=channel0+1; - control1=channel1+1; - value0=(volume0>volume1)?volume0:volume1; - value1=value0; - if (volume0==0) control0=0; - if (volume1==0) control1=0; - drvcmd[0]=CMD1_SETMODE; - drvcmd[1]=0x05; - drvcmd[3]=control0; - drvcmd[4]=value0; - drvcmd[5]=control1; - drvcmd[6]=value1; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - control0=channel0+1; - control1=channel1+1; - value0=(volume0>volume1)?volume0:volume1; - value1=value0; - if (volume0==0) control0=0; - if (volume1==0) control1=0; - drvcmd[0]=CMD2_SETMODE; - drvcmd[1]=0x0E; - drvcmd[3]=control0; - drvcmd[4]=value0; - drvcmd[5]=control1; - drvcmd[6]=value1; - flags_cmd_out=f_putcmd|f_ResponseStatus; - } - else if (famL_drive) - { - if ((volume0==0)||(channel0!=0)) control0 |= 0x80; - if ((volume1==0)||(channel1!=1)) control0 |= 0x40; - if (volume0|volume1) value0=0x80; - drvcmd[0]=CMDL_SETMODE; - drvcmd[1]=0x03; - drvcmd[4]=control0; - drvcmd[5]=value0; - flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; - } - else if (fam0_drive) /* different firmware levels */ - { - if (current_drive->drv_type>=drv_300) - { - control0=volume0&0xFC; - value0=volume1&0xFC; - if ((volume0!=0)&&(volume0<4)) control0 |= 0x04; - if ((volume1!=0)&&(volume1<4)) value0 |= 0x04; - if (channel0!=0) control0 |= 0x01; - if (channel1==1) value0 |= 0x01; - } - else - { - value0=(volume0>volume1)?volume0:volume1; - if (current_drive->drv_type<drv_211) - { - if (channel0!=0) - { - i=channel1; - channel1=channel0; - channel0=i; - i=volume1; - volume1=volume0; - volume0=i; - } - if (channel0==channel1) - { - if (channel0==0) - { - channel1=1; - volume1=0; - volume0=value0; - } - else - { - channel0=0; - volume0=0; - volume1=value0; - } - } - } - - if ((volume0!=0)&&(volume1!=0)) - { - if (volume0==0xFF) volume1=0xFF; - else if (volume1==0xFF) volume0=0xFF; - } - else if (current_drive->drv_type<drv_201) volume0=volume1=value0; - - if (current_drive->drv_type>=drv_201) - { - if (volume0==0) control0 |= 0x80; - if (volume1==0) control0 |= 0x40; - } - if (current_drive->drv_type>=drv_211) - { - if (channel0!=0) control0 |= 0x20; - if (channel1!=1) control0 |= 0x10; - } - } - drvcmd[0]=CMD0_SETMODE; - drvcmd[1]=0x83; - drvcmd[4]=control0; - drvcmd[5]=value0; - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - } - else if (famT_drive) - { - current_drive->volume_control=0; - if (!volume0) current_drive->volume_control|=0x10; - if (!volume1) current_drive->volume_control|=0x20; - i=cc_prep_mode_T(); - if (i<0) return (i); - } - if (!famT_drive) - { - response_count=0; - i=cmd_out(); - if (i<0) return (i); - } - current_drive->diskstate_flags |= volume_bit; - return (0); -} -/*==========================================================================*/ -static int GetStatus(void) -{ - int i; - - if (famT_drive) return (0); - flags_cmd_out=f_getsta|f_ResponseStatus|f_obey_p_check; - response_count=0; - cmd_type=0; - i=cmd_out(); - return (i); -} -/*==========================================================================*/ -static int cc_DriveReset(void) -{ - int i; - - msg(DBG_RES,"cc_DriveReset called.\n"); - clr_cmdbuf(); - response_count=0; - if (fam0LV_drive) OUT(CDo_reset,0x00); - else if (fam1_drive) - { - drvcmd[0]=CMD1_RESET; - flags_cmd_out=f_putcmd; - i=cmd_out(); - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_RESET; - flags_cmd_out=f_putcmd; - i=cmd_out(); - OUT(CDo_reset,0x00); - } - else if (famT_drive) - { - OUT(CDo_sel_i_d,0); - OUT(CDo_enable,current_drive->drv_sel); - OUT(CDo_command,CMDT_RESET); - for (i=1;i<10;i++) OUT(CDo_command,0); - } - if (fam0LV_drive) sbp_sleep(5*HZ); /* wait 5 seconds */ - else sbp_sleep(1*HZ); /* wait a second */ -#if 1 - if (famT_drive) - { - msg(DBG_TEA, "================CMDT_RESET given=================.\n"); - sbp_sleep(3*HZ); - } -#endif /* 1 */ - flush_status(); - i=GetStatus(); - if (i<0) return i; - if (!famT_drive) - if (current_drive->error_byte!=aud_12) return -501; - return (0); -} - -/*==========================================================================*/ -static int SetSpeed(void) -{ - int i, speed; - - if (!(current_drive->drv_options&(speed_auto|speed_300|speed_150))) return (0); - speed=speed_auto; - if (!(current_drive->drv_options&speed_auto)) - { - speed |= speed_300; - if (!(current_drive->drv_options&speed_300)) speed=0; - } - i=cc_SetSpeed(speed,0,0); - return (i); -} - -static void switch_drive(struct sbpcd_drive *); - -static int sbpcd_select_speed(struct cdrom_device_info *cdi, int speed) -{ - struct sbpcd_drive *p = cdi->handle; - if (p != current_drive) - switch_drive(p); - - return cc_SetSpeed(speed == 2 ? speed_300 : speed_150, 0, 0); -} - -/*==========================================================================*/ -static int DriveReset(void) -{ - int i; - - i=cc_DriveReset(); - if (i<0) return (-22); - do - { - i=GetStatus(); - if ((i<0)&&(i!=-ERR_DISKCHANGE)) { - return (-2); /* from sta2err */ - } - if (!st_caddy_in) break; - sbp_sleep(1); - } - while (!st_diskok); -#if 000 - current_drive->CD_changed=1; -#endif - if ((st_door_closed) && (st_caddy_in)) - { - i=DiskInfo(); - if (i<0) return (-23); - } - return (0); -} - -static int sbpcd_reset(struct cdrom_device_info *cdi) -{ - struct sbpcd_drive *p = cdi->handle; - if (p != current_drive) - switch_drive(p); - return DriveReset(); -} - -/*==========================================================================*/ -static int cc_PlayAudio(int pos_audio_start,int pos_audio_end) -{ - int i, j, n; - - if (current_drive->audio_state==audio_playing) return (-EINVAL); - clr_cmdbuf(); - response_count=0; - if (famLV_drive) - { - drvcmd[0]=CMDL_PLAY; - i=msf2blk(pos_audio_start); - n=msf2blk(pos_audio_end)+1-i; - drvcmd[1]=(i>>16)&0x00FF; - drvcmd[2]=(i>>8)&0x00FF; - drvcmd[3]=i&0x00FF; - drvcmd[4]=(n>>16)&0x00FF; - drvcmd[5]=(n>>8)&0x00FF; - drvcmd[6]=n&0x00FF; - if (famL_drive) - flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta | - f_ResponseStatus | f_obey_p_check | f_wait_if_busy; - else - flags_cmd_out = f_putcmd; - } - else - { - j=1; - if (fam1_drive) - { - drvcmd[0]=CMD1_PLAY_MSF; - flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | - f_obey_p_check | f_wait_if_busy; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_PLAY_MSF; - flags_cmd_out = f_putcmd | f_ResponseStatus | f_obey_p_check; - } - else if (famT_drive) - { - drvcmd[0]=CMDT_PLAY_MSF; - j=3; - response_count=1; - } - else if (fam0_drive) - { - drvcmd[0]=CMD0_PLAY_MSF; - flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta | - f_ResponseStatus | f_obey_p_check | f_wait_if_busy; - } - drvcmd[j]=(pos_audio_start>>16)&0x00FF; - drvcmd[j+1]=(pos_audio_start>>8)&0x00FF; - drvcmd[j+2]=pos_audio_start&0x00FF; - drvcmd[j+3]=(pos_audio_end>>16)&0x00FF; - drvcmd[j+4]=(pos_audio_end>>8)&0x00FF; - drvcmd[j+5]=pos_audio_end&0x00FF; - } - i=cmd_out(); - return (i); -} -/*==========================================================================*/ -static int cc_Pause_Resume(int pau_res) -{ - int i; - - clr_cmdbuf(); - response_count=0; - if (fam1_drive) - { - drvcmd[0]=CMD1_PAU_RES; - if (pau_res!=1) drvcmd[1]=0x80; - flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_PAU_RES; - if (pau_res!=1) drvcmd[2]=0x01; - flags_cmd_out=f_putcmd|f_ResponseStatus; - } - else if (fam0LV_drive) - { - drvcmd[0]=CMD0_PAU_RES; - if (pau_res!=1) drvcmd[1]=0x80; - if (famL_drive) - flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus| - f_obey_p_check|f_bit1; - else if (famV_drive) - flags_cmd_out=f_putcmd; - else - flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus| - f_obey_p_check; - } - else if (famT_drive) - { - if (pau_res==3) return (cc_PlayAudio(current_drive->pos_audio_start,current_drive->pos_audio_end)); - else if (pau_res==1) drvcmd[0]=CMDT_PAUSE; - else return (-56); - } - i=cmd_out(); - return (i); -} -/*==========================================================================*/ -static int cc_LockDoor(char lock) -{ - int i; - - if (fam0_drive) return (0); - msg(DBG_LCK,"cc_LockDoor: %d (drive %d)\n", lock, current_drive - D_S); - msg(DBG_LCS,"p_door_locked bit %d before\n", st_door_locked); - clr_cmdbuf(); - response_count=0; - if (fam1_drive) - { - drvcmd[0]=CMD1_LOCK_CTL; - if (lock==1) drvcmd[1]=0x01; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_LOCK_CTL; - if (lock==1) drvcmd[4]=0x01; - flags_cmd_out=f_putcmd|f_ResponseStatus; - } - else if (famLV_drive) - { - drvcmd[0]=CMDL_LOCK_CTL; - if (lock==1) drvcmd[1]=0x01; - if (famL_drive) - flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; - else - flags_cmd_out=f_putcmd; - } - else if (famT_drive) - { - drvcmd[0]=CMDT_LOCK_CTL; - if (lock==1) drvcmd[4]=0x01; - } - i=cmd_out(); - msg(DBG_LCS,"p_door_locked bit %d after\n", st_door_locked); - return (i); -} -/*==========================================================================*/ -/*==========================================================================*/ -static int UnLockDoor(void) -{ - int i,j; - - j=20; - do - { - i=cc_LockDoor(0); - --j; - sbp_sleep(1); - } - while ((i<0)&&(j)); - if (i<0) - { - cc_DriveReset(); - return -84; - } - return (0); -} -/*==========================================================================*/ -static int LockDoor(void) -{ - int i,j; - - j=20; - do - { - i=cc_LockDoor(1); - --j; - sbp_sleep(1); - } - while ((i<0)&&(j)); - if (j==0) - { - cc_DriveReset(); - j=20; - do - { - i=cc_LockDoor(1); - --j; - sbp_sleep(1); - } - while ((i<0)&&(j)); - } - return (i); -} - -static int sbpcd_lock_door(struct cdrom_device_info *cdi, int lock) -{ - return lock ? LockDoor() : UnLockDoor(); -} - -/*==========================================================================*/ -static int cc_CloseTray(void) -{ - int i; - - if (fam0_drive) return (0); - msg(DBG_LCK,"cc_CloseTray (drive %d)\n", current_drive - D_S); - msg(DBG_LCS,"p_door_closed bit %d before\n", st_door_closed); - - clr_cmdbuf(); - response_count=0; - if (fam1_drive) - { - drvcmd[0]=CMD1_TRAY_CTL; - flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_TRAY_CTL; - drvcmd[1]=0x01; - drvcmd[4]=0x03; /* "insert" */ - flags_cmd_out=f_putcmd|f_ResponseStatus; - } - else if (famLV_drive) - { - drvcmd[0]=CMDL_TRAY_CTL; - if (famLV_drive) - flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta| - f_ResponseStatus|f_obey_p_check|f_bit1; - else - flags_cmd_out=f_putcmd; - } - else if (famT_drive) - { - drvcmd[0]=CMDT_TRAY_CTL; - drvcmd[4]=0x03; /* "insert" */ - } - i=cmd_out(); - msg(DBG_LCS,"p_door_closed bit %d after\n", st_door_closed); - - i=cc_ReadError(); - flags_cmd_out |= f_respo2; - cc_ReadStatus(); /* command: give 1-byte status */ - i=ResponseStatus(); - if (famT_drive&&(i<0)) - { - cc_DriveReset(); - i=ResponseStatus(); -#if 0 - sbp_sleep(HZ); -#endif /* 0 */ - i=ResponseStatus(); - } - if (i<0) - { - msg(DBG_INF,"sbpcd cc_CloseTray: ResponseStatus timed out (%d).\n",i); - } - if (!(famT_drive)) - { - if (!st_spinning) - { - cc_SpinUp(); - if (st_check) i=cc_ReadError(); - flags_cmd_out |= f_respo2; - cc_ReadStatus(); - i=ResponseStatus(); - } else { - } - } - i=DiskInfo(); - return (i); -} - -static int sbpcd_tray_move(struct cdrom_device_info *cdi, int position) -{ - int retval=0; - switch_drive(cdi->handle); - /* DUH! --AJK */ - if(current_drive->CD_changed != 0xFF) { - current_drive->CD_changed=0xFF; - current_drive->diskstate_flags &= ~cd_size_bit; - } - if (position == 1) { - cc_SpinDown(); - } else { - retval=cc_CloseTray(); - } - return retval; -} - -/*==========================================================================*/ -static int cc_ReadSubQ(void) -{ - int i,j; - - current_drive->diskstate_flags &= ~subq_bit; - for (j=255;j>0;j--) - { - clr_cmdbuf(); - if (fam1_drive) - { - drvcmd[0]=CMD1_READSUBQ; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - response_count=11; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_READSUBQ; - drvcmd[1]=0x02; - drvcmd[3]=0x01; - flags_cmd_out=f_putcmd; - response_count=10; - } - else if (fam0LV_drive) - { - drvcmd[0]=CMD0_READSUBQ; - drvcmd[1]=0x02; - if (famLV_drive) - flags_cmd_out=f_putcmd; - else - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - response_count=13; - } - else if (famT_drive) - { - response_count=12; - drvcmd[0]=CMDT_READSUBQ; - drvcmd[1]=0x02; - drvcmd[2]=0x40; - drvcmd[3]=0x01; - drvcmd[8]=response_count; - } - i=cmd_out(); - if (i<0) return (i); - for (i=0;i<response_count;i++) - { - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_SQ1,"cc_ReadSubQ:%s\n", msgbuf); - } - if (famT_drive) break; - if (infobuf[0]!=0) break; - if ((!st_spinning) || (j==1)) - { - current_drive->SubQ_ctl_adr=current_drive->SubQ_trk=current_drive->SubQ_pnt_idx=current_drive->SubQ_whatisthis=0; - current_drive->SubQ_run_tot=current_drive->SubQ_run_trk=0; - return (0); - } - } - if (famT_drive) current_drive->SubQ_ctl_adr=infobuf[1]; - else current_drive->SubQ_ctl_adr=swap_nibbles(infobuf[1]); - current_drive->SubQ_trk=byt2bcd(infobuf[2]); - current_drive->SubQ_pnt_idx=byt2bcd(infobuf[3]); - if (fam0LV_drive) i=5; - else if (fam12_drive) i=4; - else if (famT_drive) i=8; - current_drive->SubQ_run_tot=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */ - i=7; - if (fam0LV_drive) i=9; - else if (fam12_drive) i=7; - else if (famT_drive) i=4; - current_drive->SubQ_run_trk=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */ - current_drive->SubQ_whatisthis=infobuf[i+3]; - current_drive->diskstate_flags |= subq_bit; - return (0); -} -/*==========================================================================*/ -static int cc_ModeSense(void) -{ - int i; - - if (fam2_drive) return (0); - if (famV_drive) return (0); - current_drive->diskstate_flags &= ~frame_size_bit; - clr_cmdbuf(); - if (fam1_drive) - { - response_count=5; - drvcmd[0]=CMD1_GETMODE; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam0L_drive) - { - response_count=2; - drvcmd[0]=CMD0_GETMODE; - if (famL_drive) flags_cmd_out=f_putcmd; - else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - } - else if (famT_drive) - { - response_count=10; - drvcmd[0]=CMDT_GETMODE; - drvcmd[4]=response_count; - } - i=cmd_out(); - if (i<0) return (i); - i=0; - current_drive->sense_byte=0; - if (fam1_drive) current_drive->sense_byte=infobuf[i++]; - else if (famT_drive) - { - if (infobuf[4]==0x01) current_drive->xa_byte=0x20; - else current_drive->xa_byte=0; - i=2; - } - current_drive->frame_size=make16(infobuf[i],infobuf[i+1]); - for (i=0;i<response_count;i++) - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_XA1,"cc_ModeSense:%s\n", msgbuf); - - current_drive->diskstate_flags |= frame_size_bit; - return (0); -} -/*==========================================================================*/ -/*==========================================================================*/ -static int cc_ModeSelect(int framesize) -{ - int i; - - if (fam2_drive) return (0); - if (famV_drive) return (0); - current_drive->diskstate_flags &= ~frame_size_bit; - clr_cmdbuf(); - current_drive->frame_size=framesize; - if (framesize==CD_FRAMESIZE_RAW) current_drive->sense_byte=0x82; - else current_drive->sense_byte=0x00; - - msg(DBG_XA1,"cc_ModeSelect: %02X %04X\n", - current_drive->sense_byte, current_drive->frame_size); - - if (fam1_drive) - { - drvcmd[0]=CMD1_SETMODE; - drvcmd[1]=0x00; - drvcmd[2]=current_drive->sense_byte; - drvcmd[3]=(current_drive->frame_size>>8)&0xFF; - drvcmd[4]=current_drive->frame_size&0xFF; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam0L_drive) - { - drvcmd[0]=CMD0_SETMODE; - drvcmd[1]=0x00; - drvcmd[2]=(current_drive->frame_size>>8)&0xFF; - drvcmd[3]=current_drive->frame_size&0xFF; - drvcmd[4]=0x00; - if(famL_drive) - flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check; - else - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - } - else if (famT_drive) - { - return (-1); - } - response_count=0; - i=cmd_out(); - if (i<0) return (i); - current_drive->diskstate_flags |= frame_size_bit; - return (0); -} -/*==========================================================================*/ -static int cc_GetVolume(void) -{ - int i; - u_char switches; - u_char chan0=0; - u_char vol0=0; - u_char chan1=1; - u_char vol1=0; - - if (famV_drive) return (0); - current_drive->diskstate_flags &= ~volume_bit; - clr_cmdbuf(); - if (fam1_drive) - { - drvcmd[0]=CMD1_GETMODE; - drvcmd[1]=0x05; - response_count=5; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_GETMODE; - drvcmd[1]=0x0E; - response_count=5; - flags_cmd_out=f_putcmd; - } - else if (fam0L_drive) - { - drvcmd[0]=CMD0_GETMODE; - drvcmd[1]=0x03; - response_count=2; - if(famL_drive) - flags_cmd_out=f_putcmd; - else - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - } - else if (famT_drive) - { - i=cc_get_mode_T(); - if (i<0) return (i); - } - if (!famT_drive) - { - i=cmd_out(); - if (i<0) return (i); - } - if (fam1_drive) - { - chan0=infobuf[1]&0x0F; - vol0=infobuf[2]; - chan1=infobuf[3]&0x0F; - vol1=infobuf[4]; - if (chan0==0) - { - chan0=1; - vol0=0; - } - if (chan1==0) - { - chan1=2; - vol1=0; - } - chan0 >>= 1; - chan1 >>= 1; - } - else if (fam2_drive) - { - chan0=infobuf[1]; - vol0=infobuf[2]; - chan1=infobuf[3]; - vol1=infobuf[4]; - } - else if (famL_drive) - { - chan0=0; - chan1=1; - vol0=vol1=infobuf[1]; - switches=infobuf[0]; - if ((switches&0x80)!=0) chan0=1; - if ((switches&0x40)!=0) chan1=0; - } - else if (fam0_drive) /* different firmware levels */ - { - chan0=0; - chan1=1; - vol0=vol1=infobuf[1]; - if (current_drive->drv_type>=drv_201) - { - if (current_drive->drv_type<drv_300) - { - switches=infobuf[0]; - if ((switches&0x80)!=0) vol0=0; - if ((switches&0x40)!=0) vol1=0; - if (current_drive->drv_type>=drv_211) - { - if ((switches&0x20)!=0) chan0=1; - if ((switches&0x10)!=0) chan1=0; - } - } - else - { - vol0=infobuf[0]; - if ((vol0&0x01)!=0) chan0=1; - if ((vol1&0x01)==0) chan1=0; - vol0 &= 0xFC; - vol1 &= 0xFC; - if (vol0!=0) vol0 += 3; - if (vol1!=0) vol1 += 3; - } - } - } - else if (famT_drive) - { - current_drive->volume_control=infobuf[7]; - chan0=0; - chan1=1; - if (current_drive->volume_control&0x10) vol0=0; - else vol0=0xff; - if (current_drive->volume_control&0x20) vol1=0; - else vol1=0xff; - } - current_drive->vol_chan0=chan0; - current_drive->vol_ctrl0=vol0; - current_drive->vol_chan1=chan1; - current_drive->vol_ctrl1=vol1; -#if 000 - current_drive->vol_chan2=2; - current_drive->vol_ctrl2=0xFF; - current_drive->vol_chan3=3; - current_drive->vol_ctrl3=0xFF; -#endif /* 000 */ - current_drive->diskstate_flags |= volume_bit; - return (0); -} -/*==========================================================================*/ -static int cc_ReadCapacity(void) -{ - int i, j; - - if (fam2_drive) return (0); /* some firmware lacks this command */ - if (famLV_drive) return (0); /* some firmware lacks this command */ - if (famT_drive) return (0); /* done with cc_ReadTocDescr() */ - current_drive->diskstate_flags &= ~cd_size_bit; - for (j=3;j>0;j--) - { - clr_cmdbuf(); - if (fam1_drive) - { - drvcmd[0]=CMD1_CAPACITY; - response_count=5; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } -#if 00 - else if (fam2_drive) - { - drvcmd[0]=CMD2_CAPACITY; - response_count=8; - flags_cmd_out=f_putcmd; - } -#endif - else if (fam0_drive) - { - drvcmd[0]=CMD0_CAPACITY; - response_count=5; - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - } - i=cmd_out(); - if (i>=0) break; - msg(DBG_000,"cc_ReadCapacity: cmd_out: err %d\n", i); - cc_ReadError(); - } - if (j==0) return (i); - if (fam1_drive) current_drive->CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_MSF_OFFSET; - else if (fam0_drive) current_drive->CDsize_frm=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])); -#if 00 - else if (fam2_drive) current_drive->CDsize_frm=make32(make16(infobuf[0],infobuf[1]),make16(infobuf[2],infobuf[3])); -#endif - current_drive->diskstate_flags |= cd_size_bit; - msg(DBG_000,"cc_ReadCapacity: %d frames.\n", current_drive->CDsize_frm); - return (0); -} -/*==========================================================================*/ -static int cc_ReadTocDescr(void) -{ - int i; - - current_drive->diskstate_flags &= ~toc_bit; - clr_cmdbuf(); - if (fam1_drive) - { - drvcmd[0]=CMD1_DISKINFO; - response_count=6; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam0LV_drive) - { - drvcmd[0]=CMD0_DISKINFO; - response_count=6; - if(famLV_drive) - flags_cmd_out=f_putcmd; - else - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - /* possibly longer timeout periods necessary */ - current_drive->f_multisession=0; - drvcmd[0]=CMD2_DISKINFO; - drvcmd[1]=0x02; - drvcmd[2]=0xAB; - drvcmd[3]=0xFF; /* session */ - response_count=8; - flags_cmd_out=f_putcmd; - } - else if (famT_drive) - { - current_drive->f_multisession=0; - response_count=12; - drvcmd[0]=CMDT_DISKINFO; - drvcmd[1]=0x02; - drvcmd[6]=CDROM_LEADOUT; - drvcmd[8]=response_count; - drvcmd[9]=0x00; - } - i=cmd_out(); - if (i<0) return (i); - if ((famT_drive)&&(i<response_count)) return (-100-i); - if ((fam1_drive)||(fam2_drive)||(fam0LV_drive)) - current_drive->xa_byte=infobuf[0]; - if (fam2_drive) - { - current_drive->first_session=infobuf[1]; - current_drive->last_session=infobuf[2]; - current_drive->n_first_track=infobuf[3]; - current_drive->n_last_track=infobuf[4]; - if (current_drive->first_session!=current_drive->last_session) - { - current_drive->f_multisession=1; - current_drive->lba_multi=msf2blk(make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7]))); - } -#if 0 - if (current_drive->first_session!=current_drive->last_session) - { - if (current_drive->last_session<=20) - zwanzig=current_drive->last_session+1; - else zwanzig=20; - for (count=current_drive->first_session;count<zwanzig;count++) - { - drvcmd[0]=CMD2_DISKINFO; - drvcmd[1]=0x02; - drvcmd[2]=0xAB; - drvcmd[3]=count; - response_count=8; - flags_cmd_out=f_putcmd; - i=cmd_out(); - if (i<0) return (i); - current_drive->msf_multi_n[count]=make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7])); - } - current_drive->diskstate_flags |= multisession_bit; - } -#endif - drvcmd[0]=CMD2_DISKINFO; - drvcmd[1]=0x02; - drvcmd[2]=0xAA; - drvcmd[3]=0xFF; - response_count=5; - flags_cmd_out=f_putcmd; - i=cmd_out(); - if (i<0) return (i); - current_drive->size_msf=make32(make16(0,infobuf[2]),make16(infobuf[3],infobuf[4])); - current_drive->size_blk=msf2blk(current_drive->size_msf); - current_drive->CDsize_frm=current_drive->size_blk+1; - } - else if (famT_drive) - { - current_drive->size_msf=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11])); - current_drive->size_blk=msf2blk(current_drive->size_msf); - current_drive->CDsize_frm=current_drive->size_blk+1; - current_drive->n_first_track=infobuf[2]; - current_drive->n_last_track=infobuf[3]; - } - else - { - current_drive->n_first_track=infobuf[1]; - current_drive->n_last_track=infobuf[2]; - current_drive->size_msf=make32(make16(0,infobuf[3]),make16(infobuf[4],infobuf[5])); - current_drive->size_blk=msf2blk(current_drive->size_msf); - if (famLV_drive) current_drive->CDsize_frm=current_drive->size_blk+1; - } - current_drive->diskstate_flags |= toc_bit; - msg(DBG_TOC,"TocDesc: xa %02X firstt %02X lastt %02X size %08X firstses %02X lastsess %02X\n", - current_drive->xa_byte, - current_drive->n_first_track, - current_drive->n_last_track, - current_drive->size_msf, - current_drive->first_session, - current_drive->last_session); - return (0); -} -/*==========================================================================*/ -static int cc_ReadTocEntry(int num) -{ - int i; - - clr_cmdbuf(); - if (fam1_drive) - { - drvcmd[0]=CMD1_READTOC; - drvcmd[2]=num; - response_count=8; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam2_drive) - { - /* possibly longer timeout periods necessary */ - drvcmd[0]=CMD2_DISKINFO; - drvcmd[1]=0x02; - drvcmd[2]=num; - response_count=5; - flags_cmd_out=f_putcmd; - } - else if (fam0LV_drive) - { - drvcmd[0]=CMD0_READTOC; - drvcmd[1]=0x02; - drvcmd[2]=num; - response_count=8; - if (famLV_drive) - flags_cmd_out=f_putcmd; - else - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - } - else if (famT_drive) - { - response_count=12; - drvcmd[0]=CMDT_DISKINFO; - drvcmd[1]=0x02; - drvcmd[6]=num; - drvcmd[8]=response_count; - drvcmd[9]=0x00; - } - i=cmd_out(); - if (i<0) return (i); - if ((famT_drive)&&(i<response_count)) return (-100-i); - if ((fam1_drive)||(fam0LV_drive)) - { - current_drive->TocEnt_nixbyte=infobuf[0]; - i=1; - } - else if (fam2_drive) i=0; - else if (famT_drive) i=5; - current_drive->TocEnt_ctl_adr=swap_nibbles(infobuf[i++]); - if ((fam1_drive)||(fam0L_drive)) - { - current_drive->TocEnt_number=infobuf[i++]; - current_drive->TocEnt_format=infobuf[i]; - } - else - { - current_drive->TocEnt_number=num; - current_drive->TocEnt_format=0; - } - if (fam1_drive) i=4; - else if (fam0LV_drive) i=5; - else if (fam2_drive) i=2; - else if (famT_drive) i=9; - current_drive->TocEnt_address=make32(make16(0,infobuf[i]), - make16(infobuf[i+1],infobuf[i+2])); - for (i=0;i<response_count;i++) - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_ECS,"TocEntry:%s\n", msgbuf); - msg(DBG_TOC,"TocEntry: %02X %02X %02X %02X %08X\n", - current_drive->TocEnt_nixbyte, current_drive->TocEnt_ctl_adr, - current_drive->TocEnt_number, current_drive->TocEnt_format, - current_drive->TocEnt_address); - return (0); -} -/*==========================================================================*/ -static int cc_ReadPacket(void) -{ - int i; - - clr_cmdbuf(); - drvcmd[0]=CMD0_PACKET; - drvcmd[1]=response_count; - if(famL_drive) flags_cmd_out=f_putcmd; - else if (fam01_drive) - flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; - else if (fam2_drive) return (-1); /* not implemented yet */ - else if (famT_drive) - { - return (-1); - } - i=cmd_out(); - return (i); -} -/*==========================================================================*/ -static int convert_UPC(u_char *p) -{ - int i; - - p++; - if (fam0L_drive) p[13]=0; - for (i=0;i<7;i++) - { - if (fam1_drive) current_drive->UPC_buf[i]=swap_nibbles(*p++); - else if (fam0L_drive) - { - current_drive->UPC_buf[i]=((*p++)<<4)&0xFF; - current_drive->UPC_buf[i] |= *p++; - } - else if (famT_drive) - { - return (-1); - } - else /* CD200 */ - { - return (-1); - } - } - current_drive->UPC_buf[6] &= 0xF0; - return (0); -} -/*==========================================================================*/ -static int cc_ReadUPC(void) -{ - int i; -#if TEST_UPC - int block, checksum; -#endif /* TEST_UPC */ - - if (fam2_drive) return (0); /* not implemented yet */ - if (famT_drive) return (0); /* not implemented yet */ - if (famV_drive) return (0); /* not implemented yet */ -#if 1 - if (fam0_drive) return (0); /* but it should work */ -#endif - - current_drive->diskstate_flags &= ~upc_bit; -#if TEST_UPC - for (block=CD_MSF_OFFSET+1;block<CD_MSF_OFFSET+200;block++) - { -#endif /* TEST_UPC */ - clr_cmdbuf(); - if (fam1_drive) - { - drvcmd[0]=CMD1_READ_UPC; -#if TEST_UPC - drvcmd[1]=(block>>16)&0xFF; - drvcmd[2]=(block>>8)&0xFF; - drvcmd[3]=block&0xFF; -#endif /* TEST_UPC */ - response_count=8; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam0L_drive) - { - drvcmd[0]=CMD0_READ_UPC; -#if TEST_UPC - drvcmd[2]=(block>>16)&0xFF; - drvcmd[3]=(block>>8)&0xFF; - drvcmd[4]=block&0xFF; -#endif /* TEST_UPC */ - response_count=0; - flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; - } - else if (fam2_drive) - { - return (-1); - } - else if (famT_drive) - { - return (-1); - } - i=cmd_out(); - if (i<0) - { - msg(DBG_000,"cc_ReadUPC cmd_out: err %d\n", i); - return (i); - } - if (fam0L_drive) - { - response_count=16; - if (famL_drive) flags_cmd_out=f_putcmd; - i=cc_ReadPacket(); - if (i<0) - { - msg(DBG_000,"cc_ReadUPC ReadPacket: err %d\n", i); - return (i); - } - } -#if TEST_UPC - checksum=0; -#endif /* TEST_UPC */ - for (i=0;i<(fam1_drive?8:16);i++) - { -#if TEST_UPC - checksum |= infobuf[i]; -#endif /* TEST_UPC */ - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - } - msgbuf[i*3]=0; - msg(DBG_UPC,"UPC info:%s\n", msgbuf); -#if TEST_UPC - if ((checksum&0x7F)!=0) break; - } -#endif /* TEST_UPC */ - current_drive->UPC_ctl_adr=0; - if (fam1_drive) i=0; - else i=2; - if ((infobuf[i]&0x80)!=0) - { - convert_UPC(&infobuf[i]); - current_drive->UPC_ctl_adr = (current_drive->TocEnt_ctl_adr & 0xF0) | 0x02; - } - for (i=0;i<7;i++) - sprintf(&msgbuf[i*3], " %02X", current_drive->UPC_buf[i]); - sprintf(&msgbuf[i*3], " (%02X)", current_drive->UPC_ctl_adr); - msgbuf[i*3+5]=0; - msg(DBG_UPC,"UPC code:%s\n", msgbuf); - current_drive->diskstate_flags |= upc_bit; - return (0); -} - -static int sbpcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) -{ - int i; - unsigned char *mcnp = mcn->medium_catalog_number; - unsigned char *resp; - - current_drive->diskstate_flags &= ~upc_bit; - clr_cmdbuf(); - if (fam1_drive) - { - drvcmd[0]=CMD1_READ_UPC; - response_count=8; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - } - else if (fam0L_drive) - { - drvcmd[0]=CMD0_READ_UPC; - response_count=0; - flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; - } - else if (fam2_drive) - { - return (-1); - } - else if (famT_drive) - { - return (-1); - } - i=cmd_out(); - if (i<0) - { - msg(DBG_000,"cc_ReadUPC cmd_out: err %d\n", i); - return (i); - } - if (fam0L_drive) - { - response_count=16; - if (famL_drive) flags_cmd_out=f_putcmd; - i=cc_ReadPacket(); - if (i<0) - { - msg(DBG_000,"cc_ReadUPC ReadPacket: err %d\n", i); - return (i); - } - } - current_drive->UPC_ctl_adr=0; - if (fam1_drive) i=0; - else i=2; - - resp = infobuf + i; - if (*resp++ == 0x80) { - /* packed bcd to single ASCII digits */ - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - *mcnp++ = (*resp++ & 0x0f) + '0'; - *mcnp++ = (*resp >> 4) + '0'; - } - *mcnp = '\0'; - - current_drive->diskstate_flags |= upc_bit; - return (0); -} - -/*==========================================================================*/ -static int cc_CheckMultiSession(void) -{ - int i; - - if (fam2_drive) return (0); - current_drive->f_multisession=0; - current_drive->lba_multi=0; - if (fam0_drive) return (0); - clr_cmdbuf(); - if (fam1_drive) - { - drvcmd[0]=CMD1_MULTISESS; - response_count=6; - flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; - i=cmd_out(); - if (i<0) return (i); - if ((infobuf[0]&0x80)!=0) - { - current_drive->f_multisession=1; - current_drive->lba_multi=msf2blk(make32(make16(0,infobuf[1]), - make16(infobuf[2],infobuf[3]))); - } - } - else if (famLV_drive) - { - drvcmd[0]=CMDL_MULTISESS; - drvcmd[1]=3; - drvcmd[2]=1; - response_count=8; - flags_cmd_out=f_putcmd; - i=cmd_out(); - if (i<0) return (i); - current_drive->lba_multi=msf2blk(make32(make16(0,infobuf[5]), - make16(infobuf[6],infobuf[7]))); - } - else if (famT_drive) - { - response_count=12; - drvcmd[0]=CMDT_DISKINFO; - drvcmd[1]=0x02; - drvcmd[6]=0; - drvcmd[8]=response_count; - drvcmd[9]=0x40; - i=cmd_out(); - if (i<0) return (i); - if (i<response_count) return (-100-i); - current_drive->first_session=infobuf[2]; - current_drive->last_session=infobuf[3]; - current_drive->track_of_last_session=infobuf[6]; - if (current_drive->first_session!=current_drive->last_session) - { - current_drive->f_multisession=1; - current_drive->lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11]))); - } - } - for (i=0;i<response_count;i++) - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_MUL,"MultiSession Info:%s (%d)\n", msgbuf, current_drive->lba_multi); - if (current_drive->lba_multi>200) - { - current_drive->f_multisession=1; - msg(DBG_MUL,"MultiSession base: %06X\n", current_drive->lba_multi); - } - return (0); -} -/*==========================================================================*/ -#ifdef FUTURE -static int cc_SubChanInfo(int frame, int count, u_char *buffer) - /* "frame" is a RED BOOK (msf-bin) address */ -{ - int i; - - if (fam0LV_drive) return (-ENOSYS); /* drive firmware lacks it */ - if (famT_drive) - { - return (-1); - } -#if 0 - if (current_drive->audio_state!=audio_playing) return (-ENODATA); -#endif - clr_cmdbuf(); - drvcmd[0]=CMD1_SUBCHANINF; - drvcmd[1]=(frame>>16)&0xFF; - drvcmd[2]=(frame>>8)&0xFF; - drvcmd[3]=frame&0xFF; - drvcmd[5]=(count>>8)&0xFF; - drvcmd[6]=count&0xFF; - flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; - cmd_type=READ_SC; - current_drive->frame_size=CD_FRAMESIZE_SUB; - i=cmd_out(); /* which buffer to use? */ - return (i); -} -#endif /* FUTURE */ -/*==========================================================================*/ -static void __init check_datarate(void) -{ - int i=0; - - msg(DBG_IOX,"check_datarate entered.\n"); - datarate=0; -#if TEST_STI - for (i=0;i<=1000;i++) printk("."); -#endif - /* set a timer to make (timed_out_delay!=0) after 1.1 seconds */ -#if 1 - del_timer(&delay_timer); -#endif - delay_timer.expires=jiffies+11*HZ/10; - timed_out_delay=0; - add_timer(&delay_timer); -#if 0 - msg(DBG_TIM,"delay timer started (11*HZ/10).\n"); -#endif - do - { - i=inb(CDi_status); - datarate++; -#if 1 - if (datarate>0x6FFFFFFF) break; -#endif - } - while (!timed_out_delay); - del_timer(&delay_timer); -#if 0 - msg(DBG_TIM,"datarate: %04X\n", datarate); -#endif - if (datarate<65536) datarate=65536; - maxtim16=datarate*16; - maxtim04=datarate*4; - maxtim02=datarate*2; - maxtim_8=datarate/32; -#if LONG_TIMING - maxtim_data=datarate/100; -#else - maxtim_data=datarate/300; -#endif /* LONG_TIMING */ -#if 0 - msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n", maxtim_8, maxtim_data); -#endif -} -/*==========================================================================*/ -#if 0 -static int c2_ReadError(int fam) -{ - int i; - - clr_cmdbuf(); - response_count=9; - clr_respo_buf(9); - if (fam==1) - { - drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */ - i=do_cmd(f_putcmd|f_lopsta|f_getsta|f_ResponseStatus); - } - else if (fam==2) - { - drvcmd[0]=CMD2_READ_ERR; - i=do_cmd(f_putcmd); - } - else return (-1); - return (i); -} -#endif -/*==========================================================================*/ -static void __init ask_mail(void) -{ - int i; - - msg(DBG_INF, "please mail the following lines to emoenke@gwdg.de\n"); - msg(DBG_INF, "(don't mail if you are not using the actual kernel):\n"); - msg(DBG_INF, "%s\n", VERSION); - msg(DBG_INF, "address %03X, type %s, drive %s (ID %d)\n", - CDo_command, type, current_drive->drive_model, current_drive->drv_id); - for (i=0;i<12;i++) - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_INF,"infobuf =%s\n", msgbuf); - for (i=0;i<12;i++) - sprintf(&msgbuf[i*3], " %c ", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_INF,"infobuf =%s\n", msgbuf); -} -/*==========================================================================*/ -static int __init check_version(void) -{ - int i, j, l; - int teac_possible=0; - - msg(DBG_INI,"check_version: id=%d, d=%d.\n", current_drive->drv_id, current_drive - D_S); - current_drive->drv_type=0; - - /* check for CR-52x, CR-56x, LCS-7260 and ECS-AT */ - /* clear any pending error state */ - clr_cmdbuf(); - drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */ - response_count=9; - flags_cmd_out=f_putcmd; - i=cmd_out(); - if (i<0) msg(DBG_INI,"CMD0_READ_ERR returns %d (ok anyway).\n",i); - /* read drive version */ - clr_cmdbuf(); - for (i=0;i<12;i++) infobuf[i]=0; - drvcmd[0]=CMD0_READ_VER; /* same as CMD1_ and CMDL_ */ - response_count=12; /* fam1: only 11 */ - flags_cmd_out=f_putcmd; - i=cmd_out(); - if (i<-1) msg(DBG_INI,"CMD0_READ_VER returns %d\n",i); - if (i==-11) teac_possible++; - j=0; - for (i=0;i<12;i++) j+=infobuf[i]; - if (j) - { - for (i=0;i<12;i++) - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_ECS,"infobuf =%s\n", msgbuf); - for (i=0;i<12;i++) - sprintf(&msgbuf[i*3], " %c ", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_ECS,"infobuf =%s\n", msgbuf); - } - for (i=0;i<4;i++) if (infobuf[i]!=family1[i]) break; - if (i==4) - { - current_drive->drive_model[0]='C'; - current_drive->drive_model[1]='R'; - current_drive->drive_model[2]='-'; - current_drive->drive_model[3]='5'; - current_drive->drive_model[4]=infobuf[i++]; - current_drive->drive_model[5]=infobuf[i++]; - current_drive->drive_model[6]=0; - current_drive->drv_type=drv_fam1; - } - if (!current_drive->drv_type) - { - for (i=0;i<8;i++) if (infobuf[i]!=family0[i]) break; - if (i==8) - { - current_drive->drive_model[0]='C'; - current_drive->drive_model[1]='R'; - current_drive->drive_model[2]='-'; - current_drive->drive_model[3]='5'; - current_drive->drive_model[4]='2'; - current_drive->drive_model[5]='x'; - current_drive->drive_model[6]=0; - current_drive->drv_type=drv_fam0; - } - } - if (!current_drive->drv_type) - { - for (i=0;i<8;i++) if (infobuf[i]!=familyL[i]) break; - if (i==8) - { - for (j=0;j<8;j++) - current_drive->drive_model[j]=infobuf[j]; - current_drive->drive_model[8]=0; - current_drive->drv_type=drv_famL; - } - } - if (!current_drive->drv_type) - { - for (i=0;i<6;i++) if (infobuf[i]!=familyV[i]) break; - if (i==6) - { - for (j=0;j<6;j++) - current_drive->drive_model[j]=infobuf[j]; - current_drive->drive_model[6]=0; - current_drive->drv_type=drv_famV; - i+=2; /* 2 blanks before version */ - } - } - if (!current_drive->drv_type) - { - /* check for CD200 */ - clr_cmdbuf(); - drvcmd[0]=CMD2_READ_ERR; - response_count=9; - flags_cmd_out=f_putcmd; - i=cmd_out(); - if (i<0) msg(DBG_INI,"CMD2_READERR returns %d (ok anyway).\n",i); - if (i<0) msg(DBG_000,"CMD2_READERR returns %d (ok anyway).\n",i); - /* read drive version */ - clr_cmdbuf(); - for (i=0;i<12;i++) infobuf[i]=0; - if (sbpro_type==1) OUT(CDo_sel_i_d,0); -#if 0 - OUT(CDo_reset,0); - sbp_sleep(6*HZ); - OUT(CDo_enable,current_drive->drv_sel); -#endif - drvcmd[0]=CMD2_READ_VER; - response_count=12; - flags_cmd_out=f_putcmd; - i=cmd_out(); - if (i<0) msg(DBG_INI,"CMD2_READ_VER returns %d\n",i); - if (i==-7) teac_possible++; - j=0; - for (i=0;i<12;i++) j+=infobuf[i]; - if (j) - { - for (i=0;i<12;i++) - sprintf(&msgbuf[i*3], " %02X", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_IDX,"infobuf =%s\n", msgbuf); - for (i=0;i<12;i++) - sprintf(&msgbuf[i*3], " %c ", infobuf[i]); - msgbuf[i*3]=0; - msg(DBG_IDX,"infobuf =%s\n", msgbuf); - } - if (i>=0) - { - for (i=0;i<5;i++) if (infobuf[i]!=family2[i]) break; - if (i==5) - { - current_drive->drive_model[0]='C'; - current_drive->drive_model[1]='D'; - current_drive->drive_model[2]='2'; - current_drive->drive_model[3]='0'; - current_drive->drive_model[4]='0'; - current_drive->drive_model[5]=infobuf[i++]; - current_drive->drive_model[6]=infobuf[i++]; - current_drive->drive_model[7]=0; - current_drive->drv_type=drv_fam2; - } - } - } - if (!current_drive->drv_type) - { - /* check for TEAC CD-55A */ - msg(DBG_TEA,"teac_possible: %d\n",teac_possible); - for (j=1;j<=((current_drive->drv_id==0)?3:1);j++) - { - for (l=1;l<=((current_drive->drv_id==0)?10:1);l++) - { - msg(DBG_TEA,"TEAC reset #%d-%d.\n", j, l); - if (sbpro_type==1) OUT(CDo_reset,0); - else - { - OUT(CDo_enable,current_drive->drv_sel); - OUT(CDo_sel_i_d,0); - OUT(CDo_command,CMDT_RESET); - for (i=0;i<9;i++) OUT(CDo_command,0); - } - sbp_sleep(5*HZ/10); - OUT(CDo_enable,current_drive->drv_sel); - OUT(CDo_sel_i_d,0); - i=inb(CDi_status); - msg(DBG_TEA,"TEAC CDi_status: %02X.\n",i); -#if 0 - if (i&s_not_result_ready) continue; /* drive not present or ready */ -#endif - i=inb(CDi_info); - msg(DBG_TEA,"TEAC CDi_info: %02X.\n",i); - if (i==0x55) break; /* drive found */ - } - if (i==0x55) break; /* drive found */ - } - if (i==0x55) /* drive found */ - { - msg(DBG_TEA,"TEAC drive found.\n"); - clr_cmdbuf(); - flags_cmd_out=f_putcmd; - response_count=12; - drvcmd[0]=CMDT_READ_VER; - drvcmd[4]=response_count; - for (i=0;i<12;i++) infobuf[i]=0; - i=cmd_out_T(); - if (i!=0) msg(DBG_TEA,"cmd_out_T(CMDT_READ_VER) returns %d.\n",i); - for (i=1;i<6;i++) if (infobuf[i]!=familyT[i-1]) break; - if (i==6) - { - current_drive->drive_model[0]='C'; - current_drive->drive_model[1]='D'; - current_drive->drive_model[2]='-'; - current_drive->drive_model[3]='5'; - current_drive->drive_model[4]='5'; - current_drive->drive_model[5]=0; - current_drive->drv_type=drv_famT; - } - } - } - if (!current_drive->drv_type) - { - msg(DBG_TEA,"no drive found at address %03X under ID %d.\n",CDo_command,current_drive->drv_id); - return (-522); - } - for (j=0;j<4;j++) current_drive->firmware_version[j]=infobuf[i+j]; - if (famL_drive) - { - u_char lcs_firm_e1[]="A E1"; - u_char lcs_firm_f4[]="A4F4"; - - for (j=0;j<4;j++) - if (current_drive->firmware_version[j]!=lcs_firm_e1[j]) break; - if (j==4) current_drive->drv_type=drv_e1; - - for (j=0;j<4;j++) - if (current_drive->firmware_version[j]!=lcs_firm_f4[j]) break; - if (j==4) current_drive->drv_type=drv_f4; - - if (current_drive->drv_type==drv_famL) ask_mail(); - } - else if (famT_drive) - { - j=infobuf[4]; /* one-byte version??? - here: 0x15 */ - if (j=='5') - { - current_drive->firmware_version[0]=infobuf[7]; - current_drive->firmware_version[1]=infobuf[8]; - current_drive->firmware_version[2]=infobuf[10]; - current_drive->firmware_version[3]=infobuf[11]; - } - else - { - if (j!=0x15) ask_mail(); - current_drive->firmware_version[0]='0'; - current_drive->firmware_version[1]='.'; - current_drive->firmware_version[2]='0'+(j>>4); - current_drive->firmware_version[3]='0'+(j&0x0f); - } - } - else /* CR-52x, CR-56x, CD200, ECS-AT */ - { - j = (current_drive->firmware_version[0] & 0x0F) * 100 + - (current_drive->firmware_version[2] & 0x0F) *10 + - (current_drive->firmware_version[3] & 0x0F); - if (fam0_drive) - { - if (j<200) current_drive->drv_type=drv_199; - else if (j<201) current_drive->drv_type=drv_200; - else if (j<210) current_drive->drv_type=drv_201; - else if (j<211) current_drive->drv_type=drv_210; - else if (j<300) current_drive->drv_type=drv_211; - else if (j>=300) current_drive->drv_type=drv_300; - } - else if (fam1_drive) - { - if (j<100) current_drive->drv_type=drv_099; - else - { - current_drive->drv_type=drv_100; - if ((j!=500)&&(j!=102)) ask_mail(); - } - } - else if (fam2_drive) - { - if (current_drive->drive_model[5]=='F') - { - if ((j!=1)&&(j!=35)&&(j!=200)&&(j!=210)) - ask_mail(); /* unknown version at time */ - } - else - { - msg(DBG_INF,"this CD200 drive is not fully supported yet - only audio will work.\n"); - if ((j!=101)&&(j!=35)) - ask_mail(); /* unknown version at time */ - } - } - else if (famV_drive) - { - if ((j==100)||(j==150)) current_drive->drv_type=drv_at; - ask_mail(); /* hopefully we get some feedback by this */ - } - } - msg(DBG_LCS,"drive type %02X\n",current_drive->drv_type); - msg(DBG_INI,"check_version done.\n"); - return (0); -} -/*==========================================================================*/ -static void switch_drive(struct sbpcd_drive *p) -{ - current_drive = p; - OUT(CDo_enable,current_drive->drv_sel); - msg(DBG_DID,"drive %d (ID=%d) activated.\n", - current_drive - D_S, current_drive->drv_id); - return; -} -/*==========================================================================*/ -#ifdef PATH_CHECK -/* - * probe for the presence of an interface card - */ -static int __init check_card(int port) -{ -#undef N_RESPO -#define N_RESPO 20 - int i, j, k; - u_char response[N_RESPO]; - u_char save_port0; - u_char save_port3; - - msg(DBG_INI,"check_card entered.\n"); - save_port0=inb(port+0); - save_port3=inb(port+3); - - for (j=0;j<NR_SBPCD;j++) - { - OUT(port+3,j) ; /* enable drive #j */ - OUT(port+0,CMD0_PATH_CHECK); - for (i=10;i>0;i--) OUT(port+0,0); - for (k=0;k<N_RESPO;k++) response[k]=0; - for (k=0;k<N_RESPO;k++) - { - for (i=10000;i>0;i--) - { - if (inb(port+1)&s_not_result_ready) continue; - response[k]=inb(port+0); - break; - } - } - for (i=0;i<N_RESPO;i++) - sprintf(&msgbuf[i*3], " %02X", response[i]); - msgbuf[i*3]=0; - msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf); - OUT(port+0,CMD0_PATH_CHECK); - for (i=10;i>0;i--) OUT(port+0,0); - for (k=0;k<N_RESPO;k++) response[k]=0xFF; - for (k=0;k<N_RESPO;k++) - { - for (i=10000;i>0;i--) - { - if (inb(port+1)&s_not_result_ready) continue; - response[k]=inb(port+0); - break; - } - } - for (i=0;i<N_RESPO;i++) - sprintf(&msgbuf[i*3], " %02X", response[i]); - msgbuf[i*3]=0; - msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf); - - if (response[0]==0xAA) - if (response[1]==0x55) - return (0); - } - for (j=0;j<NR_SBPCD;j++) - { - OUT(port+3,j) ; /* enable drive #j */ - OUT(port+0,CMD2_READ_VER); - for (i=10;i>0;i--) OUT(port+0,0); - for (k=0;k<N_RESPO;k++) response[k]=0; - for (k=0;k<N_RESPO;k++) - { - for (i=1000000;i>0;i--) - { - if (inb(port+1)&s_not_result_ready) continue; - response[k]=inb(port+0); - break; - } - } - for (i=0;i<N_RESPO;i++) - sprintf(&msgbuf[i*3], " %02X", response[i]); - msgbuf[i*3]=0; - msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf); - - OUT(port+0,CMD2_READ_VER); - for (i=10;i>0;i--) OUT(port+0,0); - for (k=0;k<N_RESPO;k++) response[k]=0xFF; - for (k=0;k<N_RESPO;k++) - { - for (i=1000000;i>0;i--) - { - if (inb(port+1)&s_not_result_ready) continue; - response[k]=inb(port+0); - break; - } - } - for (i=0;i<N_RESPO;i++) - sprintf(&msgbuf[i*3], " %02X", response[i]); - msgbuf[i*3]=0; - msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf); - - if (response[0]==0xAA) - if (response[1]==0x55) - return (0); - } - OUT(port+0,save_port0); - OUT(port+3,save_port3); - return (0); /* in any case - no real "function" at time */ -} -#endif /* PATH_CHECK */ -/*==========================================================================*/ -/*==========================================================================*/ -/* - * probe for the presence of drives on the selected controller - */ -static int __init check_drives(void) -{ - int i, j; - - msg(DBG_INI,"check_drives entered.\n"); - ndrives=0; - for (j=0;j<max_drives;j++) - { - struct sbpcd_drive *p = D_S + ndrives; - p->drv_id=j; - if (sbpro_type==1) p->drv_sel=(j&0x01)<<1|(j&0x02)>>1; - else p->drv_sel=j; - switch_drive(p); - msg(DBG_INI,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j); - msg(DBG_000,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j); - i=check_version(); - if (i<0) msg(DBG_INI,"check_version returns %d.\n",i); - else - { - current_drive->drv_options=drv_pattern[j]; - if (fam0L_drive) current_drive->drv_options&=~(speed_auto|speed_300|speed_150); - msg(DBG_INF, "Drive %d (ID=%d): %.9s (%.4s) at 0x%03X (type %d)\n", - current_drive - D_S, - current_drive->drv_id, - current_drive->drive_model, - current_drive->firmware_version, - CDo_command, - sbpro_type); - ndrives++; - } - } - for (j=ndrives;j<NR_SBPCD;j++) D_S[j].drv_id=-1; - if (ndrives==0) return (-1); - return (0); -} -/*==========================================================================*/ -#ifdef FUTURE -/* - * obtain if requested service disturbs current audio state - */ -static int obey_audio_state(u_char audio_state, u_char func,u_char subfunc) -{ - switch (audio_state) /* audio status from controller */ - { - case aud_11: /* "audio play in progress" */ - case audx11: - switch (func) /* DOS command code */ - { - case cmd_07: /* input flush */ - case cmd_0d: /* open device */ - case cmd_0e: /* close device */ - case cmd_0c: /* ioctl output */ - return (1); - case cmd_03: /* ioctl input */ - switch (subfunc) - /* DOS ioctl input subfunction */ - { - case cxi_00: - case cxi_06: - case cxi_09: - return (1); - default: - return (ERROR15); - } - return (1); - default: - return (ERROR15); - } - return (1); - case aud_12: /* "audio play paused" */ - case audx12: - return (1); - default: - return (2); - } -} -/*==========================================================================*/ -/* allowed is only - * ioctl_o, flush_input, open_device, close_device, - * tell_address, tell_volume, tell_capabiliti, - * tell_framesize, tell_CD_changed, tell_audio_posi - */ -static int check_allowed1(u_char func1, u_char func2) -{ -#if 000 - if (func1==ioctl_o) return (0); - if (func1==read_long) return (-1); - if (func1==read_long_prefetch) return (-1); - if (func1==seek) return (-1); - if (func1==audio_play) return (-1); - if (func1==audio_pause) return (-1); - if (func1==audio_resume) return (-1); - if (func1!=ioctl_i) return (0); - if (func2==tell_SubQ_run_tot) return (-1); - if (func2==tell_cdsize) return (-1); - if (func2==tell_TocDescrip) return (-1); - if (func2==tell_TocEntry) return (-1); - if (func2==tell_subQ_info) return (-1); - if (fam1_drive) if (func2==tell_SubChanInfo) return (-1); - if (func2==tell_UPC) return (-1); -#else - return (0); -#endif -} -/*==========================================================================*/ -static int check_allowed2(u_char func1, u_char func2) -{ -#if 000 - if (func1==read_long) return (-1); - if (func1==read_long_prefetch) return (-1); - if (func1==seek) return (-1); - if (func1==audio_play) return (-1); - if (func1!=ioctl_o) return (0); - if (fam1_drive) - { - if (func2==EjectDisk) return (-1); - if (func2==CloseTray) return (-1); - } -#else - return (0); -#endif -} -/*==========================================================================*/ -static int check_allowed3(u_char func1, u_char func2) -{ -#if 000 - if (func1==ioctl_i) - { - if (func2==tell_address) return (0); - if (func2==tell_capabiliti) return (0); - if (func2==tell_CD_changed) return (0); - if (fam0L_drive) if (func2==tell_SubChanInfo) return (0); - return (-1); - } - if (func1==ioctl_o) - { - if (func2==DriveReset) return (0); - if (fam0L_drive) - { - if (func2==EjectDisk) return (0); - if (func2==LockDoor) return (0); - if (func2==CloseTray) return (0); - } - return (-1); - } - if (func1==flush_input) return (-1); - if (func1==read_long) return (-1); - if (func1==read_long_prefetch) return (-1); - if (func1==seek) return (-1); - if (func1==audio_play) return (-1); - if (func1==audio_pause) return (-1); - if (func1==audio_resume) return (-1); -#else - return (0); -#endif -} -/*==========================================================================*/ -static int seek_pos_audio_end(void) -{ - int i; - - i=msf2blk(current_drive->pos_audio_end)-1; - if (i<0) return (-1); - i=cc_Seek(i,0); - return (i); -} -#endif /* FUTURE */ -/*==========================================================================*/ -static int ReadToC(void) -{ - int i, j; - current_drive->diskstate_flags &= ~toc_bit; - current_drive->ored_ctl_adr=0; - /* special handling of CD-I HE */ - if ((current_drive->n_first_track == 2 && current_drive->n_last_track == 2) || - current_drive->xa_byte == 0x10) - { - current_drive->TocBuffer[1].nixbyte=0; - current_drive->TocBuffer[1].ctl_adr=0x40; - current_drive->TocBuffer[1].number=1; - current_drive->TocBuffer[1].format=0; - current_drive->TocBuffer[1].address=blk2msf(0); - current_drive->ored_ctl_adr |= 0x40; - current_drive->n_first_track = 1; - current_drive->n_last_track = 1; - current_drive->xa_byte = 0x10; - j = 2; - } else - for (j=current_drive->n_first_track;j<=current_drive->n_last_track;j++) - { - i=cc_ReadTocEntry(j); - if (i<0) - { - msg(DBG_INF,"cc_ReadTocEntry(%d) returns %d.\n",j,i); - return (i); - } - current_drive->TocBuffer[j].nixbyte=current_drive->TocEnt_nixbyte; - current_drive->TocBuffer[j].ctl_adr=current_drive->TocEnt_ctl_adr; - current_drive->TocBuffer[j].number=current_drive->TocEnt_number; - current_drive->TocBuffer[j].format=current_drive->TocEnt_format; - current_drive->TocBuffer[j].address=current_drive->TocEnt_address; - current_drive->ored_ctl_adr |= current_drive->TocEnt_ctl_adr; - } - /* fake entry for LeadOut Track */ - current_drive->TocBuffer[j].nixbyte=0; - current_drive->TocBuffer[j].ctl_adr=0; - current_drive->TocBuffer[j].number=CDROM_LEADOUT; - current_drive->TocBuffer[j].format=0; - current_drive->TocBuffer[j].address=current_drive->size_msf; - - current_drive->diskstate_flags |= toc_bit; - return (0); -} -/*==========================================================================*/ -static int DiskInfo(void) -{ - int i, j; - - current_drive->mode=READ_M1; - -#undef LOOP_COUNT -#define LOOP_COUNT 10 /* needed for some "old" drives */ - - msg(DBG_000,"DiskInfo entered.\n"); - for (j=1;j<LOOP_COUNT;j++) - { -#if 0 - i=SetSpeed(); - if (i<0) - { - msg(DBG_INF,"DiskInfo: SetSpeed returns %d\n", i); - continue; - } - i=cc_ModeSense(); - if (i<0) - { - msg(DBG_INF,"DiskInfo: cc_ModeSense returns %d\n", i); - continue; - } -#endif - i=cc_ReadCapacity(); - if (i>=0) break; - msg(DBG_INF,"DiskInfo: ReadCapacity #%d returns %d\n", j, i); -#if 0 - i=cc_DriveReset(); -#endif - if (!fam0_drive && j == 2) break; - } - if (j==LOOP_COUNT) return (-33); /* give up */ - - i=cc_ReadTocDescr(); - if (i<0) - { - msg(DBG_INF,"DiskInfo: ReadTocDescr returns %d\n", i); - return (i); - } - i=ReadToC(); - if (i<0) - { - msg(DBG_INF,"DiskInfo: ReadToC returns %d\n", i); - return (i); - } - i=cc_CheckMultiSession(); - if (i<0) - { - msg(DBG_INF,"DiskInfo: cc_CheckMultiSession returns %d\n", i); - return (i); - } - if (current_drive->f_multisession) current_drive->sbp_bufsiz=1; /* possibly a weird PhotoCD */ - else current_drive->sbp_bufsiz=buffers; - i=cc_ReadTocEntry(current_drive->n_first_track); - if (i<0) - { - msg(DBG_INF,"DiskInfo: cc_ReadTocEntry(1) returns %d\n", i); - return (i); - } - i=cc_ReadUPC(); - if (i<0) msg(DBG_INF,"DiskInfo: cc_ReadUPC returns %d\n", i); - if ((fam0L_drive) && (current_drive->xa_byte==0x20 || current_drive->xa_byte == 0x10)) - { - /* XA disk with old drive */ - cc_ModeSelect(CD_FRAMESIZE_RAW1); - cc_ModeSense(); - } - if (famT_drive) cc_prep_mode_T(); - msg(DBG_000,"DiskInfo done.\n"); - return (0); -} - -static int sbpcd_drive_status(struct cdrom_device_info *cdi, int slot_nr) -{ - struct sbpcd_drive *p = cdi->handle; - int st; - - if (CDSL_CURRENT != slot_nr) { - /* we have no changer support */ - return -EINVAL; - } - - cc_ReadStatus(); - st=ResponseStatus(); - if (st<0) - { - msg(DBG_INF,"sbpcd_drive_status: timeout.\n"); - return (0); - } - msg(DBG_000,"Drive Status: door_locked =%d.\n", st_door_locked); - msg(DBG_000,"Drive Status: door_closed =%d.\n", st_door_closed); - msg(DBG_000,"Drive Status: caddy_in =%d.\n", st_caddy_in); - msg(DBG_000,"Drive Status: disk_ok =%d.\n", st_diskok); - msg(DBG_000,"Drive Status: spinning =%d.\n", st_spinning); - msg(DBG_000,"Drive Status: busy =%d.\n", st_busy); - -#if 0 - if (!(p->status_bits & p_door_closed)) return CDS_TRAY_OPEN; - if (p->status_bits & p_disk_ok) return CDS_DISC_OK; - if (p->status_bits & p_disk_in) return CDS_DRIVE_NOT_READY; - - return CDS_NO_DISC; -#else - if (p->status_bits & p_spinning) return CDS_DISC_OK; -/* return CDS_TRAY_OPEN; */ - return CDS_NO_DISC; - -#endif - -} - - -/*==========================================================================*/ -#ifdef FUTURE -/* - * called always if driver gets entered - * returns 0 or ERROR2 or ERROR15 - */ -static int prepare(u_char func, u_char subfunc) -{ - int i; - - if (fam0L_drive) - { - i=inb(CDi_status); - if (i&s_attention) GetStatus(); - } - else if (fam1_drive) GetStatus(); - else if (fam2_drive) GetStatus(); - else if (famT_drive) GetStatus(); - if (current_drive->CD_changed==0xFF) - { - current_drive->diskstate_flags=0; - current_drive->audio_state=0; - if (!st_diskok) - { - i=check_allowed1(func,subfunc); - if (i<0) return (-2); - } - else - { - i=check_allowed3(func,subfunc); - if (i<0) - { - current_drive->CD_changed=1; - return (-15); - } - } - } - else - { - if (!st_diskok) - { - current_drive->diskstate_flags=0; - current_drive->audio_state=0; - i=check_allowed1(func,subfunc); - if (i<0) return (-2); - } - else - { - if (st_busy) - { - if (current_drive->audio_state!=audio_pausing) - { - i=check_allowed2(func,subfunc); - if (i<0) return (-2); - } - } - else - { - if (current_drive->audio_state==audio_playing) seek_pos_audio_end(); - current_drive->audio_state=0; - } - if (!frame_size_valid) - { - i=DiskInfo(); - if (i<0) - { - current_drive->diskstate_flags=0; - current_drive->audio_state=0; - i=check_allowed1(func,subfunc); - if (i<0) return (-2); - } - } - } - } - return (0); -} -#endif /* FUTURE */ -/*==========================================================================*/ -/*==========================================================================*/ -/* - * Check the results of the "get status" command. - */ -static int sbp_status(void) -{ - int st; - - st=ResponseStatus(); - if (st<0) - { - msg(DBG_INF,"sbp_status: timeout.\n"); - return (0); - } - - if (!st_spinning) msg(DBG_SPI,"motor got off - ignoring.\n"); - - if (st_check) - { - msg(DBG_INF,"st_check detected - retrying.\n"); - return (0); - } - if (!st_door_closed) - { - msg(DBG_INF,"door is open - retrying.\n"); - return (0); - } - if (!st_caddy_in) - { - msg(DBG_INF,"disk removed - retrying.\n"); - return (0); - } - if (!st_diskok) - { - msg(DBG_INF,"!st_diskok detected - retrying.\n"); - return (0); - } - if (st_busy) - { - msg(DBG_INF,"st_busy detected - retrying.\n"); - return (0); - } - return (1); -} -/*==========================================================================*/ - -static int sbpcd_get_last_session(struct cdrom_device_info *cdi, struct cdrom_multisession *ms_infp) -{ - struct sbpcd_drive *p = cdi->handle; - ms_infp->addr_format = CDROM_LBA; - ms_infp->addr.lba = p->lba_multi; - if (p->f_multisession) - ms_infp->xa_flag=1; /* valid redirection address */ - else - ms_infp->xa_flag=0; /* invalid redirection address */ - - return 0; -} - -static int sbpcd_audio_ioctl(struct cdrom_device_info *cdi, u_int cmd, - void * arg) -{ - struct sbpcd_drive *p = cdi->handle; - int i, st, j; - - msg(DBG_IO2,"ioctl(%s, 0x%08lX, 0x%08p)\n", cdi->name, cmd, arg); - if (p->drv_id==-1) { - msg(DBG_INF, "ioctl: bad device: %s\n", cdi->name); - return (-ENXIO); /* no such drive */ - } - down(&ioctl_read_sem); - if (p != current_drive) - switch_drive(p); - - msg(DBG_IO2,"ioctl: device %s, request %04X\n",cdi->name,cmd); - switch (cmd) /* Sun-compatible */ - { - - case CDROMPAUSE: /* Pause the drive */ - msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n"); - /* pause the drive unit when it is currently in PLAY mode, */ - /* or reset the starting and ending locations when in PAUSED mode. */ - /* If applicable, at the next stopping point it reaches */ - /* the drive will discontinue playing. */ - switch (current_drive->audio_state) - { - case audio_playing: - if (famL_drive) i=cc_ReadSubQ(); - else i=cc_Pause_Resume(1); - if (i<0) RETURN_UP(-EIO); - if (famL_drive) i=cc_Pause_Resume(1); - else i=cc_ReadSubQ(); - if (i<0) RETURN_UP(-EIO); - current_drive->pos_audio_start=current_drive->SubQ_run_tot; - current_drive->audio_state=audio_pausing; - RETURN_UP(0); - case audio_pausing: - i=cc_Seek(current_drive->pos_audio_start,1); - if (i<0) RETURN_UP(-EIO); - RETURN_UP(0); - default: - RETURN_UP(-EINVAL); - } - - case CDROMRESUME: /* resume paused audio play */ - msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n"); - /* resume playing audio tracks when a previous PLAY AUDIO call has */ - /* been paused with a PAUSE command. */ - /* It will resume playing from the location saved in SubQ_run_tot. */ - if (current_drive->audio_state!=audio_pausing) RETURN_UP(-EINVAL); - if (famL_drive) - i=cc_PlayAudio(current_drive->pos_audio_start, - current_drive->pos_audio_end); - else i=cc_Pause_Resume(3); - if (i<0) RETURN_UP(-EIO); - current_drive->audio_state=audio_playing; - RETURN_UP(0); - - case CDROMPLAYMSF: - msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n"); -#ifdef SAFE_MIXED - if (current_drive->has_data>1) RETURN_UP(-EBUSY); -#endif /* SAFE_MIXED */ - if (current_drive->audio_state==audio_playing) - { - i=cc_Pause_Resume(1); - if (i<0) RETURN_UP(-EIO); - i=cc_ReadSubQ(); - if (i<0) RETURN_UP(-EIO); - current_drive->pos_audio_start=current_drive->SubQ_run_tot; - i=cc_Seek(current_drive->pos_audio_start,1); - } - memcpy(&msf, (void *) arg, sizeof(struct cdrom_msf)); - /* values come as msf-bin */ - current_drive->pos_audio_start = (msf.cdmsf_min0<<16) | - (msf.cdmsf_sec0<<8) | - msf.cdmsf_frame0; - current_drive->pos_audio_end = (msf.cdmsf_min1<<16) | - (msf.cdmsf_sec1<<8) | - msf.cdmsf_frame1; - msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n", - current_drive->pos_audio_start,current_drive->pos_audio_end); - i=cc_PlayAudio(current_drive->pos_audio_start,current_drive->pos_audio_end); - if (i<0) - { - msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i); - DriveReset(); - current_drive->audio_state=0; - RETURN_UP(-EIO); - } - current_drive->audio_state=audio_playing; - RETURN_UP(0); - - case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n"); -#ifdef SAFE_MIXED - if (current_drive->has_data>1) RETURN_UP(-EBUSY); -#endif /* SAFE_MIXED */ - if (current_drive->audio_state==audio_playing) - { - msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n"); -#if 1 - RETURN_UP(0); /* just let us play on */ -#else - RETURN_UP(-EINVAL); /* play on, but say "error" */ -#endif - } - memcpy(&ti,(void *) arg,sizeof(struct cdrom_ti)); - msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n", - ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1); - if (ti.cdti_trk0<current_drive->n_first_track) RETURN_UP(-EINVAL); - if (ti.cdti_trk0>current_drive->n_last_track) RETURN_UP(-EINVAL); - if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0; - if (ti.cdti_trk1>current_drive->n_last_track) ti.cdti_trk1=current_drive->n_last_track; - current_drive->pos_audio_start=current_drive->TocBuffer[ti.cdti_trk0].address; - current_drive->pos_audio_end=current_drive->TocBuffer[ti.cdti_trk1+1].address; - i=cc_PlayAudio(current_drive->pos_audio_start,current_drive->pos_audio_end); - if (i<0) - { - msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i); - DriveReset(); - current_drive->audio_state=0; - RETURN_UP(-EIO); - } - current_drive->audio_state=audio_playing; - RETURN_UP(0); - - case CDROMREADTOCHDR: /* Read the table of contents header */ - msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n"); - tochdr.cdth_trk0=current_drive->n_first_track; - tochdr.cdth_trk1=current_drive->n_last_track; - memcpy((void *) arg, &tochdr, sizeof(struct cdrom_tochdr)); - RETURN_UP(0); - - case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ - msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n"); - memcpy(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry)); - i=tocentry.cdte_track; - if (i==CDROM_LEADOUT) i=current_drive->n_last_track+1; - else if (i<current_drive->n_first_track||i>current_drive->n_last_track) - RETURN_UP(-EINVAL); - tocentry.cdte_adr=current_drive->TocBuffer[i].ctl_adr&0x0F; - tocentry.cdte_ctrl=(current_drive->TocBuffer[i].ctl_adr>>4)&0x0F; - tocentry.cdte_datamode=current_drive->TocBuffer[i].format; - if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */ - { - tocentry.cdte_addr.msf.minute=(current_drive->TocBuffer[i].address>>16)&0x00FF; - tocentry.cdte_addr.msf.second=(current_drive->TocBuffer[i].address>>8)&0x00FF; - tocentry.cdte_addr.msf.frame=current_drive->TocBuffer[i].address&0x00FF; - } - else if (tocentry.cdte_format==CDROM_LBA) /* blk required */ - tocentry.cdte_addr.lba=msf2blk(current_drive->TocBuffer[i].address); - else RETURN_UP(-EINVAL); - memcpy((void *) arg, &tocentry, sizeof(struct cdrom_tocentry)); - RETURN_UP(0); - - case CDROMSTOP: /* Spin down the drive */ - msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n"); -#ifdef SAFE_MIXED - if (current_drive->has_data>1) RETURN_UP(-EBUSY); -#endif /* SAFE_MIXED */ - i=cc_Pause_Resume(1); - current_drive->audio_state=0; -#if 0 - cc_DriveReset(); -#endif - RETURN_UP(i); - - case CDROMSTART: /* Spin up the drive */ - msg(DBG_IOC,"ioctl: CDROMSTART entered.\n"); - cc_SpinUp(); - current_drive->audio_state=0; - RETURN_UP(0); - - case CDROMVOLCTRL: /* Volume control */ - msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n"); - memcpy(&volctrl,(char *) arg,sizeof(volctrl)); - current_drive->vol_chan0=0; - current_drive->vol_ctrl0=volctrl.channel0; - current_drive->vol_chan1=1; - current_drive->vol_ctrl1=volctrl.channel1; - i=cc_SetVolume(); - RETURN_UP(0); - - case CDROMVOLREAD: /* read Volume settings from drive */ - msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n"); - st=cc_GetVolume(); - if (st<0) RETURN_UP(st); - volctrl.channel0=current_drive->vol_ctrl0; - volctrl.channel1=current_drive->vol_ctrl1; - volctrl.channel2=0; - volctrl.channel2=0; - memcpy((void *)arg,&volctrl,sizeof(volctrl)); - RETURN_UP(0); - - case CDROMSUBCHNL: /* Get subchannel info */ - msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n"); - /* Bogus, I can do better than this! --AJK - if ((st_spinning)||(!subq_valid)) { - i=cc_ReadSubQ(); - if (i<0) RETURN_UP(-EIO); - } - */ - i=cc_ReadSubQ(); - if (i<0) { - j=cc_ReadError(); /* clear out error status from drive */ - current_drive->audio_state=CDROM_AUDIO_NO_STATUS; - /* get and set the disk state here, - probably not the right place, but who cares! - It makes it work properly! --AJK */ - if (current_drive->CD_changed==0xFF) { - msg(DBG_000,"Disk changed detect\n"); - current_drive->diskstate_flags &= ~cd_size_bit; - } - RETURN_UP(-EIO); - } - if (current_drive->CD_changed==0xFF) { - /* reread the TOC because the disk has changed! --AJK */ - msg(DBG_000,"Disk changed STILL detected, rereading TOC!\n"); - i=DiskInfo(); - if(i==0) { - current_drive->CD_changed=0x00; /* cd has changed, procede, */ - RETURN_UP(-EIO); /* and get TOC, etc on next try! --AJK */ - } else { - RETURN_UP(-EIO); /* we weren't ready yet! --AJK */ - } - } - memcpy(&SC, (void *) arg, sizeof(struct cdrom_subchnl)); - /* - This virtual crap is very bogus! - It doesn't detect when the cd is done playing audio! - Lets do this right with proper hardware register reading! - */ - cc_ReadStatus(); - i=ResponseStatus(); - msg(DBG_000,"Drive Status: door_locked =%d.\n", st_door_locked); - msg(DBG_000,"Drive Status: door_closed =%d.\n", st_door_closed); - msg(DBG_000,"Drive Status: caddy_in =%d.\n", st_caddy_in); - msg(DBG_000,"Drive Status: disk_ok =%d.\n", st_diskok); - msg(DBG_000,"Drive Status: spinning =%d.\n", st_spinning); - msg(DBG_000,"Drive Status: busy =%d.\n", st_busy); - /* st_busy indicates if it's _ACTUALLY_ playing audio */ - switch (current_drive->audio_state) - { - case audio_playing: - if(st_busy==0) { - /* CD has stopped playing audio --AJK */ - current_drive->audio_state=audio_completed; - SC.cdsc_audiostatus=CDROM_AUDIO_COMPLETED; - } else { - SC.cdsc_audiostatus=CDROM_AUDIO_PLAY; - } - break; - case audio_pausing: - SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED; - break; - case audio_completed: - SC.cdsc_audiostatus=CDROM_AUDIO_COMPLETED; - break; - default: - SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS; - break; - } - SC.cdsc_adr=current_drive->SubQ_ctl_adr; - SC.cdsc_ctrl=current_drive->SubQ_ctl_adr>>4; - SC.cdsc_trk=bcd2bin(current_drive->SubQ_trk); - SC.cdsc_ind=bcd2bin(current_drive->SubQ_pnt_idx); - if (SC.cdsc_format==CDROM_LBA) - { - SC.cdsc_absaddr.lba=msf2blk(current_drive->SubQ_run_tot); - SC.cdsc_reladdr.lba=msf2blk(current_drive->SubQ_run_trk); - } - else /* not only if (SC.cdsc_format==CDROM_MSF) */ - { - SC.cdsc_absaddr.msf.minute=(current_drive->SubQ_run_tot>>16)&0x00FF; - SC.cdsc_absaddr.msf.second=(current_drive->SubQ_run_tot>>8)&0x00FF; - SC.cdsc_absaddr.msf.frame=current_drive->SubQ_run_tot&0x00FF; - SC.cdsc_reladdr.msf.minute=(current_drive->SubQ_run_trk>>16)&0x00FF; - SC.cdsc_reladdr.msf.second=(current_drive->SubQ_run_trk>>8)&0x00FF; - SC.cdsc_reladdr.msf.frame=current_drive->SubQ_run_trk&0x00FF; - } - memcpy((void *) arg, &SC, sizeof(struct cdrom_subchnl)); - msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n", - SC.cdsc_format,SC.cdsc_audiostatus, - SC.cdsc_adr,SC.cdsc_ctrl, - SC.cdsc_trk,SC.cdsc_ind, - SC.cdsc_absaddr,SC.cdsc_reladdr); - RETURN_UP(0); - - default: - msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd); - RETURN_UP(-EINVAL); - } /* end switch(cmd) */ -} -/*==========================================================================*/ -/* - * Take care of the different block sizes between cdrom and Linux. - */ -static void sbp_transfer(struct request *req) -{ - long offs; - - while ( (req->nr_sectors > 0) && - (req->sector/4 >= current_drive->sbp_first_frame) && - (req->sector/4 <= current_drive->sbp_last_frame) ) - { - offs = (req->sector - current_drive->sbp_first_frame * 4) * 512; - memcpy(req->buffer, current_drive->sbp_buf + offs, 512); - req->nr_sectors--; - req->sector++; - req->buffer += 512; - } -} -/*==========================================================================*/ -/* - * special end_request for sbpcd to solve CURRENT==NULL bug. (GTL) - * GTL = Gonzalo Tornaria <tornaria@cmat.edu.uy> - * - * This is a kludge so we don't need to modify end_request. - * We put the req we take out after INIT_REQUEST in the requests list, - * so that end_request will discard it. - * - * The bug could be present in other block devices, perhaps we - * should modify INIT_REQUEST and end_request instead, and - * change every block device.. - * - * Could be a race here?? Could e.g. a timer interrupt schedule() us? - * If so, we should copy end_request here, and do it right.. (or - * modify end_request and the block devices). - * - * In any case, the race here would be much small than it was, and - * I couldn't reproduce.. - * - * The race could be: suppose CURRENT==NULL. We put our req in the list, - * and we are scheduled. Other process takes over, and gets into - * do_sbpcd_request. It sees CURRENT!=NULL (it is == to our req), so - * proceeds. It ends, so CURRENT is now NULL.. Now we awake somewhere in - * end_request, but now CURRENT==NULL... oops! - * - */ -#undef DEBUG_GTL - -/*==========================================================================*/ -/* - * I/O request routine, called from Linux kernel. - */ -static void do_sbpcd_request(request_queue_t * q) -{ - u_int block; - u_int nsect; - int status_tries, data_tries; - struct request *req; - struct sbpcd_drive *p; -#ifdef DEBUG_GTL - static int xx_nr=0; - int xnr; -#endif - - request_loop: -#ifdef DEBUG_GTL - xnr=++xx_nr; - - req = elv_next_request(q); - - if (!req) - { - printk( "do_sbpcd_request[%di](NULL), Pid:%d, Time:%li\n", - xnr, current->pid, jiffies); - printk( "do_sbpcd_request[%do](NULL) end 0 (null), Time:%li\n", - xnr, jiffies); - return; - } - - printk(" do_sbpcd_request[%di](%p:%ld+%ld), Pid:%d, Time:%li\n", - xnr, req, req->sector, req->nr_sectors, current->pid, jiffies); -#endif - - req = elv_next_request(q); /* take out our request so no other */ - if (!req) - return; - - if (req -> sector == -1) - end_request(req, 0); - spin_unlock_irq(q->queue_lock); - - down(&ioctl_read_sem); - if (rq_data_dir(elv_next_request(q)) != READ) - { - msg(DBG_INF, "bad cmd %d\n", req->cmd[0]); - goto err_done; - } - p = req->rq_disk->private_data; -#if OLD_BUSY - while (busy_audio) sbp_sleep(HZ); /* wait a bit */ - busy_data=1; -#endif /* OLD_BUSY */ - - if (p->audio_state==audio_playing) goto err_done; - if (p != current_drive) - switch_drive(p); - - block = req->sector; /* always numbered as 512-byte-pieces */ - nsect = req->nr_sectors; /* always counted as 512-byte-pieces */ - - msg(DBG_BSZ,"read sector %d (%d sectors)\n", block, nsect); -#if 0 - msg(DBG_MUL,"read LBA %d\n", block/4); -#endif - - sbp_transfer(req); - /* if we satisfied the request from the buffer, we're done. */ - if (req->nr_sectors == 0) - { -#ifdef DEBUG_GTL - printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 2, Time:%li\n", - xnr, req, req->sector, req->nr_sectors, jiffies); -#endif - up(&ioctl_read_sem); - spin_lock_irq(q->queue_lock); - end_request(req, 1); - goto request_loop; - } - -#ifdef FUTURE - i=prepare(0,0); /* at moment not really a hassle check, but ... */ - if (i!=0) - msg(DBG_INF,"\"prepare\" tells error %d -- ignored\n", i); -#endif /* FUTURE */ - - if (!st_spinning) cc_SpinUp(); - - for (data_tries=n_retries; data_tries > 0; data_tries--) - { - for (status_tries=3; status_tries > 0; status_tries--) - { - flags_cmd_out |= f_respo3; - cc_ReadStatus(); - if (sbp_status() != 0) break; - if (st_check) cc_ReadError(); - sbp_sleep(1); /* wait a bit, try again */ - } - if (status_tries == 0) - { - msg(DBG_INF,"sbp_status: failed after 3 tries in line %d\n", __LINE__); - break; - } - - sbp_read_cmd(req); - sbp_sleep(0); - if (sbp_data(req) != 0) - { -#ifdef SAFE_MIXED - current_drive->has_data=2; /* is really a data disk */ -#endif /* SAFE_MIXED */ -#ifdef DEBUG_GTL - printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 3, Time:%li\n", - xnr, req, req->sector, req->nr_sectors, jiffies); -#endif - up(&ioctl_read_sem); - spin_lock_irq(q->queue_lock); - end_request(req, 1); - goto request_loop; - } - } - - err_done: -#if OLD_BUSY - busy_data=0; -#endif /* OLD_BUSY */ -#ifdef DEBUG_GTL - printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 4 (error), Time:%li\n", - xnr, req, req->sector, req->nr_sectors, jiffies); -#endif - up(&ioctl_read_sem); - sbp_sleep(0); /* wait a bit, try again */ - spin_lock_irq(q->queue_lock); - end_request(req, 0); - goto request_loop; -} -/*==========================================================================*/ -/* - * build and send the READ command. - */ -static void sbp_read_cmd(struct request *req) -{ -#undef OLD - - int i; - int block; - - current_drive->sbp_first_frame=current_drive->sbp_last_frame=-1; /* purge buffer */ - current_drive->sbp_current = 0; - block=req->sector/4; - if (block+current_drive->sbp_bufsiz <= current_drive->CDsize_frm) - current_drive->sbp_read_frames = current_drive->sbp_bufsiz; - else - { - current_drive->sbp_read_frames=current_drive->CDsize_frm-block; - /* avoid reading past end of data */ - if (current_drive->sbp_read_frames < 1) - { - msg(DBG_INF,"requested frame %d, CD size %d ???\n", - block, current_drive->CDsize_frm); - current_drive->sbp_read_frames=1; - } - } - - flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check; - clr_cmdbuf(); - if (famV_drive) - { - drvcmd[0]=CMDV_READ; - lba2msf(block,&drvcmd[1]); /* msf-bcd format required */ - bin2bcdx(&drvcmd[1]); - bin2bcdx(&drvcmd[2]); - bin2bcdx(&drvcmd[3]); - drvcmd[4]=current_drive->sbp_read_frames>>8; - drvcmd[5]=current_drive->sbp_read_frames&0xff; - drvcmd[6]=0x02; /* flag "msf-bcd" */ - } - else if (fam0L_drive) - { - flags_cmd_out |= f_lopsta | f_getsta | f_bit1; - if (current_drive->xa_byte==0x20) - { - cmd_type=READ_M2; - drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */ - drvcmd[1]=(block>>16)&0x0ff; - drvcmd[2]=(block>>8)&0x0ff; - drvcmd[3]=block&0x0ff; - drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff; - drvcmd[5]=current_drive->sbp_read_frames&0x0ff; - } - else - { - drvcmd[0]=CMD0_READ; /* "read frames", old drives */ - if (current_drive->drv_type>=drv_201) - { - lba2msf(block,&drvcmd[1]); /* msf-bcd format required */ - bin2bcdx(&drvcmd[1]); - bin2bcdx(&drvcmd[2]); - bin2bcdx(&drvcmd[3]); - } - else - { - drvcmd[1]=(block>>16)&0x0ff; - drvcmd[2]=(block>>8)&0x0ff; - drvcmd[3]=block&0x0ff; - } - drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff; - drvcmd[5]=current_drive->sbp_read_frames&0x0ff; - drvcmd[6]=(current_drive->drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */ - } - } - else if (fam1_drive) - { - drvcmd[0]=CMD1_READ; - lba2msf(block,&drvcmd[1]); /* msf-bin format required */ - drvcmd[5]=(current_drive->sbp_read_frames>>8)&0x0ff; - drvcmd[6]=current_drive->sbp_read_frames&0x0ff; - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_READ; - lba2msf(block,&drvcmd[1]); /* msf-bin format required */ - drvcmd[4]=(current_drive->sbp_read_frames>>8)&0x0ff; - drvcmd[5]=current_drive->sbp_read_frames&0x0ff; - drvcmd[6]=0x02; - } - else if (famT_drive) - { - drvcmd[0]=CMDT_READ; - drvcmd[2]=(block>>24)&0x0ff; - drvcmd[3]=(block>>16)&0x0ff; - drvcmd[4]=(block>>8)&0x0ff; - drvcmd[5]=block&0x0ff; - drvcmd[7]=(current_drive->sbp_read_frames>>8)&0x0ff; - drvcmd[8]=current_drive->sbp_read_frames&0x0ff; - } - flags_cmd_out=f_putcmd; - response_count=0; - i=cmd_out(); - if (i<0) msg(DBG_INF,"error giving READ command: %0d\n", i); - return; -} -/*==========================================================================*/ -/* - * Check the completion of the read-data command. On success, read - * the current_drive->sbp_bufsiz * 2048 bytes of data from the disk into buffer. - */ -static int sbp_data(struct request *req) -{ - int i=0, j=0, l, frame; - u_int try=0; - u_long timeout; - u_char *p; - u_int data_tries = 0; - u_int data_waits = 0; - u_int data_retrying = 0; - int error_flag; - int xa_count; - int max_latency; - int success; - int wait; - int duration; - - error_flag=0; - success=0; -#if LONG_TIMING - max_latency=9*HZ; -#else - if (current_drive->f_multisession) max_latency=15*HZ; - else max_latency=5*HZ; -#endif - duration=jiffies; - for (frame=0;frame<current_drive->sbp_read_frames&&!error_flag; frame++) - { - SBPCD_CLI; - - del_timer(&data_timer); - data_timer.expires=jiffies+max_latency; - timed_out_data=0; - add_timer(&data_timer); - while (!timed_out_data) - { - if (current_drive->f_multisession) try=maxtim_data*4; - else try=maxtim_data; - msg(DBG_000,"sbp_data: CDi_status loop: try=%d.\n",try); - for ( ; try!=0;try--) - { - j=inb(CDi_status); - if (!(j&s_not_data_ready)) break; - if (!(j&s_not_result_ready)) break; - if (fam0LV_drive) if (j&s_attention) break; - } - if (!(j&s_not_data_ready)) goto data_ready; - if (try==0) - { - if (data_retrying == 0) data_waits++; - data_retrying = 1; - msg(DBG_000,"sbp_data: CDi_status loop: sleeping.\n"); - sbp_sleep(1); - try = 1; - } - } - msg(DBG_INF,"sbp_data: CDi_status loop expired.\n"); - data_ready: - del_timer(&data_timer); - - if (timed_out_data) - { - msg(DBG_INF,"sbp_data: CDi_status timeout (timed_out_data) (%02X).\n", j); - error_flag++; - } - if (try==0) - { - msg(DBG_INF,"sbp_data: CDi_status timeout (try=0) (%02X).\n", j); - error_flag++; - } - if (!(j&s_not_result_ready)) - { - msg(DBG_INF, "sbp_data: RESULT_READY where DATA_READY awaited (%02X).\n", j); - response_count=20; - j=ResponseInfo(); - j=inb(CDi_status); - } - if (j&s_not_data_ready) - { - if ((current_drive->ored_ctl_adr&0x40)==0) - msg(DBG_INF, "CD contains no data tracks.\n"); - else msg(DBG_INF, "sbp_data: DATA_READY timeout (%02X).\n", j); - error_flag++; - } - SBPCD_STI; - if (error_flag) break; - - msg(DBG_000, "sbp_data: beginning to read.\n"); - p = current_drive->sbp_buf + frame * CD_FRAMESIZE; - if (sbpro_type==1) OUT(CDo_sel_i_d,1); - if (cmd_type==READ_M2) { - if (do_16bit) insw(CDi_data, xa_head_buf, CD_XA_HEAD>>1); - else insb(CDi_data, xa_head_buf, CD_XA_HEAD); - } - if (do_16bit) insw(CDi_data, p, CD_FRAMESIZE>>1); - else insb(CDi_data, p, CD_FRAMESIZE); - if (cmd_type==READ_M2) { - if (do_16bit) insw(CDi_data, xa_tail_buf, CD_XA_TAIL>>1); - else insb(CDi_data, xa_tail_buf, CD_XA_TAIL); - } - current_drive->sbp_current++; - if (sbpro_type==1) OUT(CDo_sel_i_d,0); - if (cmd_type==READ_M2) - { - for (xa_count=0;xa_count<CD_XA_HEAD;xa_count++) - sprintf(&msgbuf[xa_count*3], " %02X", xa_head_buf[xa_count]); - msgbuf[xa_count*3]=0; - msg(DBG_XA1,"xa head:%s\n", msgbuf); - } - data_retrying = 0; - data_tries++; - if (data_tries >= 1000) - { - msg(DBG_INF,"sbp_data() statistics: %d waits in %d frames.\n", data_waits, data_tries); - data_waits = data_tries = 0; - } - } - duration=jiffies-duration; - msg(DBG_TEA,"time to read %d frames: %d jiffies .\n",frame,duration); - if (famT_drive) - { - wait=8; - do - { - if (teac==2) - { - if ((i=CDi_stat_loop_T()) == -1) break; - } - else - { - sbp_sleep(1); - OUT(CDo_sel_i_d,0); - i=inb(CDi_status); - } - if (!(i&s_not_data_ready)) - { - OUT(CDo_sel_i_d,1); - j=0; - do - { - if (do_16bit) i=inw(CDi_data); - else i=inb(CDi_data); - j++; - i=inb(CDi_status); - } - while (!(i&s_not_data_ready)); - msg(DBG_TEA, "==========too much data (%d bytes/words)==============.\n", j); - } - if (!(i&s_not_result_ready)) - { - OUT(CDo_sel_i_d,0); - l=0; - do - { - infobuf[l++]=inb(CDi_info); - i=inb(CDi_status); - } - while (!(i&s_not_result_ready)); - if (infobuf[0]==0x00) success=1; -#if 1 - for (j=0;j<l;j++) sprintf(&msgbuf[j*3], " %02X", infobuf[j]); - msgbuf[j*3]=0; - msg(DBG_TEA,"sbp_data info response:%s\n", msgbuf); -#endif - if (infobuf[0]==0x02) - { - error_flag++; - do - { - ++recursion; - if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (sbp_data): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",recursion); - else msg(DBG_TEA,"sbp_data: CMDT_READ_ERR necessary.\n"); - clr_cmdbuf(); - drvcmd[0]=CMDT_READ_ERR; - j=cmd_out_T(); /* !!! recursive here !!! */ - --recursion; - sbp_sleep(1); - } - while (j<0); - current_drive->error_state=infobuf[2]; - current_drive->b3=infobuf[3]; - current_drive->b4=infobuf[4]; - } - break; - } - else - { -#if 0 - msg(DBG_TEA, "============= waiting for result=================.\n"); - sbp_sleep(1); -#endif - } - } - while (wait--); - } - - if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */ - { - msg(DBG_TEA, "================error flag: %d=================.\n", error_flag); - msg(DBG_INF,"sbp_data: read aborted by drive.\n"); -#if 1 - i=cc_DriveReset(); /* ugly fix to prevent a hang */ -#else - i=cc_ReadError(); -#endif - return (0); - } - - if (fam0LV_drive) - { - SBPCD_CLI; - i=maxtim_data; - for (timeout=jiffies+HZ; time_before(jiffies, timeout); timeout--) - { - for ( ;i!=0;i--) - { - j=inb(CDi_status); - if (!(j&s_not_data_ready)) break; - if (!(j&s_not_result_ready)) break; - if (j&s_attention) break; - } - if (i != 0 || time_after_eq(jiffies, timeout)) break; - sbp_sleep(0); - i = 1; - } - if (i==0) msg(DBG_INF,"status timeout after READ.\n"); - if (!(j&s_attention)) - { - msg(DBG_INF,"sbp_data: timeout waiting DRV_ATTN - retrying.\n"); - i=cc_DriveReset(); /* ugly fix to prevent a hang */ - SBPCD_STI; - return (0); - } - SBPCD_STI; - } - -#if 0 - if (!success) -#endif - do - { - if (fam0LV_drive) cc_ReadStatus(); -#if 1 - if (famT_drive) msg(DBG_TEA, "================before ResponseStatus=================.\n", i); -#endif - i=ResponseStatus(); /* builds status_bits, returns orig. status (old) or faked p_success (new) */ -#if 1 - if (famT_drive) msg(DBG_TEA, "================ResponseStatus: %d=================.\n", i); -#endif - if (i<0) - { - msg(DBG_INF,"bad cc_ReadStatus after read: %02X\n", current_drive->status_bits); - return (0); - } - } - while ((fam0LV_drive)&&(!st_check)&&(!(i&p_success))); - if (st_check) - { - i=cc_ReadError(); - msg(DBG_INF,"cc_ReadError was necessary after read: %d\n",i); - return (0); - } - if (fatal_err) - { - fatal_err=0; - current_drive->sbp_first_frame=current_drive->sbp_last_frame=-1; /* purge buffer */ - current_drive->sbp_current = 0; - msg(DBG_INF,"sbp_data: fatal_err - retrying.\n"); - return (0); - } - - current_drive->sbp_first_frame = req -> sector / 4; - current_drive->sbp_last_frame = current_drive->sbp_first_frame + current_drive->sbp_read_frames - 1; - sbp_transfer(req); - return (1); -} -/*==========================================================================*/ - -static int sbpcd_block_open(struct inode *inode, struct file *file) -{ - struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data; - return cdrom_open(p->sbpcd_infop, inode, file); -} - -static int sbpcd_block_release(struct inode *inode, struct file *file) -{ - struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data; - return cdrom_release(p->sbpcd_infop, file); -} - -static int sbpcd_block_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) -{ - struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data; - struct cdrom_device_info *cdi = p->sbpcd_infop; - int ret, i; - - ret = cdrom_ioctl(file, p->sbpcd_infop, inode, cmd, arg); - if (ret != -ENOSYS) - return ret; - - msg(DBG_IO2,"ioctl(%s, 0x%08lX, 0x%08lX)\n", cdi->name, cmd, arg); - if (p->drv_id==-1) { - msg(DBG_INF, "ioctl: bad device: %s\n", cdi->name); - return (-ENXIO); /* no such drive */ - } - down(&ioctl_read_sem); - if (p != current_drive) - switch_drive(p); - - msg(DBG_IO2,"ioctl: device %s, request %04X\n",cdi->name,cmd); - switch (cmd) /* Sun-compatible */ - { - case DDIOCSDBG: /* DDI Debug */ - if (!capable(CAP_SYS_ADMIN)) RETURN_UP(-EPERM); - i=sbpcd_dbg_ioctl(arg,1); - RETURN_UP(i); - case CDROMRESET: /* hard reset the drive */ - msg(DBG_IOC,"ioctl: CDROMRESET entered.\n"); - i=DriveReset(); - current_drive->audio_state=0; - RETURN_UP(i); - - case CDROMREADMODE1: - msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n"); -#ifdef SAFE_MIXED - if (current_drive->has_data>1) RETURN_UP(-EBUSY); -#endif /* SAFE_MIXED */ - cc_ModeSelect(CD_FRAMESIZE); - cc_ModeSense(); - current_drive->mode=READ_M1; - RETURN_UP(0); - - case CDROMREADMODE2: /* not usable at the moment */ - msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n"); -#ifdef SAFE_MIXED - if (current_drive->has_data>1) RETURN_UP(-EBUSY); -#endif /* SAFE_MIXED */ - cc_ModeSelect(CD_FRAMESIZE_RAW1); - cc_ModeSense(); - current_drive->mode=READ_M2; - RETURN_UP(0); - - case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */ - msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n"); - if (current_drive->sbp_audsiz>0) - vfree(current_drive->aud_buf); - current_drive->aud_buf=NULL; - current_drive->sbp_audsiz=arg; - - if (current_drive->sbp_audsiz>16) - { - current_drive->sbp_audsiz = 0; - RETURN_UP(current_drive->sbp_audsiz); - } - - if (current_drive->sbp_audsiz>0) - { - current_drive->aud_buf=(u_char *) vmalloc(current_drive->sbp_audsiz*CD_FRAMESIZE_RAW); - if (current_drive->aud_buf==NULL) - { - msg(DBG_INF,"audio buffer (%d frames) not available.\n",current_drive->sbp_audsiz); - current_drive->sbp_audsiz=0; - } - else msg(DBG_INF,"audio buffer size: %d frames.\n",current_drive->sbp_audsiz); - } - RETURN_UP(current_drive->sbp_audsiz); - - case CDROMREADAUDIO: - { /* start of CDROMREADAUDIO */ - int i=0, j=0, frame, block=0; - u_int try=0; - u_long timeout; - u_char *p; - u_int data_tries = 0; - u_int data_waits = 0; - u_int data_retrying = 0; - int status_tries; - int error_flag; - - msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n"); - if (fam0_drive) RETURN_UP(-EINVAL); - if (famL_drive) RETURN_UP(-EINVAL); - if (famV_drive) RETURN_UP(-EINVAL); - if (famT_drive) RETURN_UP(-EINVAL); -#ifdef SAFE_MIXED - if (current_drive->has_data>1) RETURN_UP(-EBUSY); -#endif /* SAFE_MIXED */ - if (current_drive->aud_buf==NULL) RETURN_UP(-EINVAL); - if (copy_from_user(&read_audio, (void __user *)arg, - sizeof(struct cdrom_read_audio))) - RETURN_UP(-EFAULT); - if (read_audio.nframes < 0 || read_audio.nframes>current_drive->sbp_audsiz) RETURN_UP(-EINVAL); - if (!access_ok(VERIFY_WRITE, read_audio.buf, - read_audio.nframes*CD_FRAMESIZE_RAW)) - RETURN_UP(-EFAULT); - - if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */ - block=msf2lba(&read_audio.addr.msf.minute); - else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */ - block=read_audio.addr.lba; - else RETURN_UP(-EINVAL); -#if 000 - i=cc_SetSpeed(speed_150,0,0); - if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i); -#endif - msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n", - block, blk2msf(block)); - msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n"); -#if OLD_BUSY - while (busy_data) sbp_sleep(HZ/10); /* wait a bit */ - busy_audio=1; -#endif /* OLD_BUSY */ - error_flag=0; - for (data_tries=5; data_tries>0; data_tries--) - { - msg(DBG_AUD,"data_tries=%d ...\n", data_tries); - current_drive->mode=READ_AU; - cc_ModeSelect(CD_FRAMESIZE_RAW); - cc_ModeSense(); - for (status_tries=3; status_tries > 0; status_tries--) - { - flags_cmd_out |= f_respo3; - cc_ReadStatus(); - if (sbp_status() != 0) break; - if (st_check) cc_ReadError(); - sbp_sleep(1); /* wait a bit, try again */ - } - if (status_tries == 0) - { - msg(DBG_AUD,"read_audio: sbp_status: failed after 3 tries in line %d.\n", __LINE__); - continue; - } - msg(DBG_AUD,"read_audio: sbp_status: ok.\n"); - - flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check; - if (fam0L_drive) - { - flags_cmd_out |= f_lopsta | f_getsta | f_bit1; - cmd_type=READ_M2; - drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */ - drvcmd[1]=(block>>16)&0x000000ff; - drvcmd[2]=(block>>8)&0x000000ff; - drvcmd[3]=block&0x000000ff; - drvcmd[4]=0; - drvcmd[5]=read_audio.nframes; /* # of frames */ - drvcmd[6]=0; - } - else if (fam1_drive) - { - drvcmd[0]=CMD1_READ; /* "read frames", new drives */ - lba2msf(block,&drvcmd[1]); /* msf-bin format required */ - drvcmd[4]=0; - drvcmd[5]=0; - drvcmd[6]=read_audio.nframes; /* # of frames */ - } - else if (fam2_drive) - { - drvcmd[0]=CMD2_READ_XA2; - lba2msf(block,&drvcmd[1]); /* msf-bin format required */ - drvcmd[4]=0; - drvcmd[5]=read_audio.nframes; /* # of frames */ - drvcmd[6]=0x11; /* raw mode */ - } - else if (famT_drive) /* CD-55A: not tested yet */ - { - } - msg(DBG_AUD,"read_audio: before giving \"read\" command.\n"); - flags_cmd_out=f_putcmd; - response_count=0; - i=cmd_out(); - if (i<0) msg(DBG_INF,"error giving READ AUDIO command: %0d\n", i); - sbp_sleep(0); - msg(DBG_AUD,"read_audio: after giving \"read\" command.\n"); - for (frame=1;frame<2 && !error_flag; frame++) - { - try=maxtim_data; - for (timeout=jiffies+9*HZ; ; ) - { - for ( ; try!=0;try--) - { - j=inb(CDi_status); - if (!(j&s_not_data_ready)) break; - if (!(j&s_not_result_ready)) break; - if (fam0L_drive) if (j&s_attention) break; - } - if (try != 0 || time_after_eq(jiffies, timeout)) break; - if (data_retrying == 0) data_waits++; - data_retrying = 1; - sbp_sleep(1); - try = 1; - } - if (try==0) - { - msg(DBG_INF,"read_audio: sbp_data: CDi_status timeout.\n"); - error_flag++; - break; - } - msg(DBG_AUD,"read_audio: sbp_data: CDi_status ok.\n"); - if (j&s_not_data_ready) - { - msg(DBG_INF, "read_audio: sbp_data: DATA_READY timeout.\n"); - error_flag++; - break; - } - msg(DBG_AUD,"read_audio: before reading data.\n"); - error_flag=0; - p = current_drive->aud_buf; - if (sbpro_type==1) OUT(CDo_sel_i_d,1); - if (do_16bit) - { - u_short *p2 = (u_short *) p; - - for (; (u_char *) p2 < current_drive->aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;) - { - if ((inb_p(CDi_status)&s_not_data_ready)) continue; - - /* get one sample */ - *p2++ = inw_p(CDi_data); - *p2++ = inw_p(CDi_data); - } - } else { - for (; p < current_drive->aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;) - { - if ((inb_p(CDi_status)&s_not_data_ready)) continue; - - /* get one sample */ - *p++ = inb_p(CDi_data); - *p++ = inb_p(CDi_data); - *p++ = inb_p(CDi_data); - *p++ = inb_p(CDi_data); - } - } - if (sbpro_type==1) OUT(CDo_sel_i_d,0); - data_retrying = 0; - } - msg(DBG_AUD,"read_audio: after reading data.\n"); - if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */ - { - msg(DBG_AUD,"read_audio: read aborted by drive\n"); -#if 0000 - i=cc_DriveReset(); /* ugly fix to prevent a hang */ -#else - i=cc_ReadError(); -#endif - continue; - } - if (fam0L_drive) - { - i=maxtim_data; - for (timeout=jiffies+9*HZ; time_before(jiffies, timeout); timeout--) - { - for ( ;i!=0;i--) - { - j=inb(CDi_status); - if (!(j&s_not_data_ready)) break; - if (!(j&s_not_result_ready)) break; - if (j&s_attention) break; - } - if (i != 0 || time_after_eq(jiffies, timeout)) break; - sbp_sleep(0); - i = 1; - } - if (i==0) msg(DBG_AUD,"read_audio: STATUS TIMEOUT AFTER READ"); - if (!(j&s_attention)) - { - msg(DBG_AUD,"read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n"); - i=cc_DriveReset(); /* ugly fix to prevent a hang */ - continue; - } - } - do - { - if (fam0L_drive) cc_ReadStatus(); - i=ResponseStatus(); /* builds status_bits, returns orig. status (old) or faked p_success (new) */ - if (i<0) { msg(DBG_AUD, - "read_audio: cc_ReadStatus error after read: %02X\n", - current_drive->status_bits); - continue; /* FIXME */ - } - } - while ((fam0L_drive)&&(!st_check)&&(!(i&p_success))); - if (st_check) - { - i=cc_ReadError(); - msg(DBG_AUD,"read_audio: cc_ReadError was necessary after read: %02X\n",i); - continue; - } - if (copy_to_user(read_audio.buf, - current_drive->aud_buf, - read_audio.nframes * CD_FRAMESIZE_RAW)) - RETURN_UP(-EFAULT); - msg(DBG_AUD,"read_audio: copy_to_user done.\n"); - break; - } - cc_ModeSelect(CD_FRAMESIZE); - cc_ModeSense(); - current_drive->mode=READ_M1; -#if OLD_BUSY - busy_audio=0; -#endif /* OLD_BUSY */ - if (data_tries == 0) - { - msg(DBG_AUD,"read_audio: failed after 5 tries in line %d.\n", __LINE__); - RETURN_UP(-EIO); - } - msg(DBG_AUD,"read_audio: successful return.\n"); - RETURN_UP(0); - } /* end of CDROMREADAUDIO */ - - default: - msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd); - RETURN_UP(-EINVAL); - } /* end switch(cmd) */ -} - -static int sbpcd_block_media_changed(struct gendisk *disk) -{ - struct sbpcd_drive *p = disk->private_data; - return cdrom_media_changed(p->sbpcd_infop); -} - -static struct block_device_operations sbpcd_bdops = -{ - .owner = THIS_MODULE, - .open = sbpcd_block_open, - .release = sbpcd_block_release, - .ioctl = sbpcd_block_ioctl, - .media_changed = sbpcd_block_media_changed, -}; -/*==========================================================================*/ -/* - * Open the device special file. Check that a disk is in. Read TOC. - */ -static int sbpcd_open(struct cdrom_device_info *cdi, int purpose) -{ - struct sbpcd_drive *p = cdi->handle; - - down(&ioctl_read_sem); - switch_drive(p); - - /* - * try to keep an "open" counter here and lock the door if 0->1. - */ - msg(DBG_LCK,"open_count: %d -> %d\n", - current_drive->open_count,current_drive->open_count+1); - if (++current_drive->open_count<=1) - { - int i; - i=LockDoor(); - current_drive->open_count=1; - if (famT_drive) msg(DBG_TEA,"sbpcd_open: before i=DiskInfo();.\n"); - i=DiskInfo(); - if (famT_drive) msg(DBG_TEA,"sbpcd_open: after i=DiskInfo();.\n"); - if ((current_drive->ored_ctl_adr&0x40)==0) - { - msg(DBG_INF,"CD contains no data tracks.\n"); -#ifdef SAFE_MIXED - current_drive->has_data=0; -#endif /* SAFE_MIXED */ - } -#ifdef SAFE_MIXED - else if (current_drive->has_data<1) current_drive->has_data=1; -#endif /* SAFE_MIXED */ - } - if (!st_spinning) cc_SpinUp(); - RETURN_UP(0); -} -/*==========================================================================*/ -/* - * On close, we flush all sbp blocks from the buffer cache. - */ -static void sbpcd_release(struct cdrom_device_info * cdi) -{ - struct sbpcd_drive *p = cdi->handle; - - if (p->drv_id==-1) { - msg(DBG_INF, "release: bad device: %s\n", cdi->name); - return; - } - down(&ioctl_read_sem); - switch_drive(p); - /* - * try to keep an "open" counter here and unlock the door if 1->0. - */ - msg(DBG_LCK,"open_count: %d -> %d\n", - p->open_count,p->open_count-1); - if (p->open_count>-2) /* CDROMEJECT may have been done */ - { - if (--p->open_count<=0) - { - p->sbp_first_frame=p->sbp_last_frame=-1; - if (p->audio_state!=audio_playing) - if (p->f_eject) cc_SpinDown(); - p->diskstate_flags &= ~cd_size_bit; - p->open_count=0; -#ifdef SAFE_MIXED - p->has_data=0; -#endif /* SAFE_MIXED */ - } - } - up(&ioctl_read_sem); - return ; -} -/*==========================================================================*/ -/* - * - */ -static int sbpcd_media_changed( struct cdrom_device_info *cdi, int disc_nr); -static struct cdrom_device_ops sbpcd_dops = { - .open = sbpcd_open, - .release = sbpcd_release, - .drive_status = sbpcd_drive_status, - .media_changed = sbpcd_media_changed, - .tray_move = sbpcd_tray_move, - .lock_door = sbpcd_lock_door, - .select_speed = sbpcd_select_speed, - .get_last_session = sbpcd_get_last_session, - .get_mcn = sbpcd_get_mcn, - .reset = sbpcd_reset, - .audio_ioctl = sbpcd_audio_ioctl, - .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | - CDC_MULTI_SESSION | CDC_MEDIA_CHANGED | - CDC_MCN | CDC_PLAY_AUDIO, - .n_minors = 1, -}; - -/*==========================================================================*/ -/* - * accept "kernel command line" parameters - * (suggested by Peter MacDonald with SLS 1.03) - * - * This is only implemented for the first controller. Should be enough to - * allow installing with a "strange" distribution kernel. - * - * use: tell LILO: - * sbpcd=0x230,SoundBlaster - * or - * sbpcd=0x300,LaserMate - * or - * sbpcd=0x338,SoundScape - * or - * sbpcd=0x2C0,Teac16bit - * - * (upper/lower case sensitive here - but all-lowercase is ok!!!). - * - * the address value has to be the CDROM PORT ADDRESS - - * not the soundcard base address. - * For the SPEA/SoundScape setup, DO NOT specify the "configuration port" - * address, but the address which is really used for the CDROM (usually 8 - * bytes above). - * - */ - -int sbpcd_setup(char *s) -{ -#ifndef MODULE - int p[4]; - (void)get_options(s, ARRAY_SIZE(p), p); - setup_done++; - msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s); - sbpro_type=0; /* default: "LaserMate" */ - if (p[0]>1) sbpro_type=p[2]; - else if (!strcmp(s,str_sb)) sbpro_type=1; - else if (!strcmp(s,str_sb_l)) sbpro_type=1; - else if (!strcmp(s,str_sp)) sbpro_type=2; - else if (!strcmp(s,str_sp_l)) sbpro_type=2; - else if (!strcmp(s,str_ss)) sbpro_type=2; - else if (!strcmp(s,str_ss_l)) sbpro_type=2; - else if (!strcmp(s,str_t16)) sbpro_type=3; - else if (!strcmp(s,str_t16_l)) sbpro_type=3; - if (p[0]>0) sbpcd_ioaddr=p[1]; - if (p[0]>2) max_drives=p[3]; -#else - sbpcd_ioaddr = sbpcd[0]; - sbpro_type = sbpcd[1]; -#endif - - CDo_command=sbpcd_ioaddr; - CDi_info=sbpcd_ioaddr; - CDi_status=sbpcd_ioaddr+1; - CDo_sel_i_d=sbpcd_ioaddr+1; - CDo_reset=sbpcd_ioaddr+2; - CDo_enable=sbpcd_ioaddr+3; - f_16bit=0; - if ((sbpro_type==1)||(sbpro_type==3)) - { - CDi_data=sbpcd_ioaddr; - if (sbpro_type==3) - { - f_16bit=1; - sbpro_type=1; - } - } - else CDi_data=sbpcd_ioaddr+2; - - return 1; -} - -__setup("sbpcd=", sbpcd_setup); - - -/*==========================================================================*/ -/* - * Sequoia S-1000 CD-ROM Interface Configuration - * as used within SPEA Media FX, Ensonic SoundScape and some Reveal cards - * The soundcard has to get jumpered for the interface type "Panasonic" - * (not Sony or Mitsumi) and to get soft-configured for - * -> configuration port address - * -> CDROM port offset (num_ports): has to be 8 here. Possibly this - * offset value determines the interface type (none, Panasonic, - * Mitsumi, Sony). - * The interface uses a configuration port (0x320, 0x330, 0x340, 0x350) - * some bytes below the real CDROM address. - * - * For the Panasonic style (LaserMate) interface and the configuration - * port 0x330, we have to use an offset of 8; so, the real CDROM port - * address is 0x338. - */ -static int __init config_spea(void) -{ - /* - * base address offset between configuration port and CDROM port, - * this probably defines the interface type - * 2 (type=??): 0x00 - * 8 (type=LaserMate):0x10 - * 16 (type=??):0x20 - * 32 (type=??):0x30 - */ - int n_ports=0x10; - - int irq_number=0; /* off:0x00, 2/9:0x01, 7:0x03, 12:0x05, 15:0x07 */ - int dma_channel=0; /* off: 0x00, 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68 */ - int dack_polarity=0; /* L:0x00, H:0x80 */ - int drq_polarity=0x40; /* L:0x00, H:0x40 */ - int i; - -#define SPEA_REG_1 sbpcd_ioaddr-0x08+4 -#define SPEA_REG_2 sbpcd_ioaddr-0x08+5 - - OUT(SPEA_REG_1,0xFF); - i=inb(SPEA_REG_1); - if (i!=0x0F) - { - msg(DBG_SEQ,"no SPEA interface at %04X present.\n", sbpcd_ioaddr); - return (-1); /* no interface found */ - } - OUT(SPEA_REG_1,0x04); - OUT(SPEA_REG_2,0xC0); - - OUT(SPEA_REG_1,0x05); - OUT(SPEA_REG_2,0x10|drq_polarity|dack_polarity); - -#if 1 -#define SPEA_PATTERN 0x80 -#else -#define SPEA_PATTERN 0x00 -#endif - OUT(SPEA_REG_1,0x06); - OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN); - OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN); - - OUT(SPEA_REG_1,0x09); - i=(inb(SPEA_REG_2)&0xCF)|n_ports; - OUT(SPEA_REG_2,i); - - sbpro_type = 0; /* acts like a LaserMate interface now */ - msg(DBG_SEQ,"found SoundScape interface at %04X.\n", sbpcd_ioaddr); - return (0); -} - -/*==========================================================================*/ -/* - * Test for presence of drive and initialize it. - * Called once at boot or load time. - */ - -/* FIXME: cleanups after failed allocations are too ugly for words */ -#ifdef MODULE -int __init __sbpcd_init(void) -#else -int __init sbpcd_init(void) -#endif -{ - int i=0, j=0; - int addr[2]={1, CDROM_PORT}; - int port_index; - - sti(); - - msg(DBG_INF,"sbpcd.c %s\n", VERSION); -#ifndef MODULE -#if DISTRIBUTION - if (!setup_done) - { - msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, Longshine, TEAC CD-ROM drives\n"); - msg(DBG_INF,"= = = = = = = = = = W A R N I N G = = = = = = = = = =\n"); - msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an NE2000 card).\n"); - msg(DBG_INF,"If that happens, you have to reboot and use the\n"); - msg(DBG_INF,"LILO (kernel) command line feature like:\n"); - msg(DBG_INF," LILO boot: ... sbpcd=0x230,SoundBlaster\n"); - msg(DBG_INF,"or like:\n"); - msg(DBG_INF," LILO boot: ... sbpcd=0x300,LaserMate\n"); - msg(DBG_INF,"or like:\n"); - msg(DBG_INF," LILO boot: ... sbpcd=0x338,SoundScape\n"); - msg(DBG_INF,"with your REAL address.\n"); - msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = == = = =\n"); - } -#endif /* DISTRIBUTION */ - sbpcd[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */ - sbpcd[1]=sbpro_type; /* possibly changed by kernel command line */ -#endif /* MODULE */ - - for (port_index=0;port_index<NUM_PROBE;port_index+=2) - { - addr[1]=sbpcd[port_index]; - if (addr[1]==0) break; - if (check_region(addr[1],4)) - { - msg(DBG_INF,"check_region: %03X is not free.\n",addr[1]); - continue; - } - if (sbpcd[port_index+1]==2) type=str_sp; - else if (sbpcd[port_index+1]==1) type=str_sb; - else if (sbpcd[port_index+1]==3) type=str_t16; - else type=str_lm; - sbpcd_setup((char *)type); -#if DISTRIBUTION - msg(DBG_INF,"Scanning 0x%X (%s)...\n", CDo_command, type); -#endif /* DISTRIBUTION */ - if (sbpcd[port_index+1]==2) - { - i=config_spea(); - if (i<0) continue; - } -#ifdef PATH_CHECK - if (check_card(addr[1])) continue; -#endif /* PATH_CHECK */ - i=check_drives(); - msg(DBG_INI,"check_drives done.\n"); - if (i>=0) break; /* drive found */ - } /* end of cycling through the set of possible I/O port addresses */ - - if (ndrives==0) - { - msg(DBG_INF, "No drive found.\n"); -#ifdef MODULE - return -EIO; -#else - goto init_done; -#endif /* MODULE */ - } - - if (port_index>0) - { - msg(DBG_INF, "You should read Documentation/cdrom/sbpcd\n"); - msg(DBG_INF, "and then configure sbpcd.h for your hardware.\n"); - } - check_datarate(); - msg(DBG_INI,"check_datarate done.\n"); - - for (j=0;j<NR_SBPCD;j++) - { - struct sbpcd_drive *p = D_S + j; - if (p->drv_id==-1) - continue; - switch_drive(p); -#if 1 - if (!famL_drive) cc_DriveReset(); -#endif - if (!st_spinning) cc_SpinUp(); - p->sbp_first_frame = -1; /* First frame in buffer */ - p->sbp_last_frame = -1; /* Last frame in buffer */ - p->sbp_read_frames = 0; /* Number of frames being read to buffer */ - p->sbp_current = 0; /* Frame being currently read */ - p->CD_changed=1; - p->frame_size=CD_FRAMESIZE; - p->f_eject=0; -#if EJECT - if (!fam0_drive) p->f_eject=1; -#endif /* EJECT */ - cc_ReadStatus(); - i=ResponseStatus(); /* returns orig. status or p_busy_new */ - if (famT_drive) i=ResponseStatus(); /* returns orig. status or p_busy_new */ - if (i<0) - { - if (i!=-402) - msg(DBG_INF,"init: ResponseStatus returns %d.\n",i); - } - else - { - if (st_check) - { - i=cc_ReadError(); - msg(DBG_INI,"init: cc_ReadError returns %d\n",i); - } - } - msg(DBG_INI,"init: first GetStatus: %d\n",i); - msg(DBG_LCS,"init: first GetStatus: error_byte=%d\n", - p->error_byte); - if (p->error_byte==aud_12) - { - timeout=jiffies+2*HZ; - do - { - i=GetStatus(); - msg(DBG_INI,"init: second GetStatus: %02X\n",i); - msg(DBG_LCS, - "init: second GetStatus: error_byte=%d\n", - p->error_byte); - if (i<0) break; - if (!st_caddy_in) break; - } - while ((!st_diskok)||time_after(jiffies, timeout)); - } - i=SetSpeed(); - if (i>=0) p->CD_changed=1; - } - - if (!request_region(CDo_command,4,major_name)) - { - printk(KERN_WARNING "sbpcd: Unable to request region 0x%x\n", CDo_command); - return -EIO; - } - - /* - * Turn on the CD audio channels. - * The addresses are obtained from SOUND_BASE (see sbpcd.h). - */ -#if SOUND_BASE - OUT(MIXER_addr,MIXER_CD_Volume); /* select SB Pro mixer register */ - OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */ -#endif /* SOUND_BASE */ - - if (register_blkdev(MAJOR_NR, major_name)) { -#ifdef MODULE - return -EIO; -#else - goto init_done; -#endif /* MODULE */ - } - - /* - * init error handling is broken beyond belief in this driver... - */ - sbpcd_queue = blk_init_queue(do_sbpcd_request, &sbpcd_lock); - if (!sbpcd_queue) { - release_region(CDo_command,4); - unregister_blkdev(MAJOR_NR, major_name); - return -ENOMEM; - } - - for (j=0;j<NR_SBPCD;j++) - { - struct cdrom_device_info * sbpcd_infop; - struct gendisk *disk; - struct sbpcd_drive *p = D_S + j; - - if (p->drv_id==-1) continue; - switch_drive(p); -#ifdef SAFE_MIXED - p->has_data=0; -#endif /* SAFE_MIXED */ - /* - * allocate memory for the frame buffers - */ - p->aud_buf=NULL; - p->sbp_audsiz=0; - p->sbp_bufsiz=buffers; - if (p->drv_type&drv_fam1) - if (READ_AUDIO>0) - p->sbp_audsiz = READ_AUDIO; - p->sbp_buf=(u_char *) vmalloc(buffers*CD_FRAMESIZE); - if (!p->sbp_buf) { - msg(DBG_INF,"data buffer (%d frames) not available.\n", - buffers); - if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL)) - { - printk("Can't unregister %s\n", major_name); - } - release_region(CDo_command,4); - blk_cleanup_queue(sbpcd_queue); - return -EIO; - } -#ifdef MODULE - msg(DBG_INF,"data buffer size: %d frames.\n",buffers); -#endif /* MODULE */ - if (p->sbp_audsiz>0) - { - p->aud_buf=(u_char *) vmalloc(p->sbp_audsiz*CD_FRAMESIZE_RAW); - if (p->aud_buf==NULL) msg(DBG_INF,"audio buffer (%d frames) not available.\n",p->sbp_audsiz); - else msg(DBG_INF,"audio buffer size: %d frames.\n",p->sbp_audsiz); - } - sbpcd_infop = vmalloc(sizeof (struct cdrom_device_info)); - if (sbpcd_infop == NULL) - { - release_region(CDo_command,4); - blk_cleanup_queue(sbpcd_queue); - return -ENOMEM; - } - memset(sbpcd_infop, 0, sizeof(struct cdrom_device_info)); - sbpcd_infop->ops = &sbpcd_dops; - sbpcd_infop->speed = 2; - sbpcd_infop->capacity = 1; - sprintf(sbpcd_infop->name, "sbpcd%d", j); - sbpcd_infop->handle = p; - p->sbpcd_infop = sbpcd_infop; - disk = alloc_disk(1); - disk->major = MAJOR_NR; - disk->first_minor = j; - disk->fops = &sbpcd_bdops; - strcpy(disk->disk_name, sbpcd_infop->name); - disk->flags = GENHD_FL_CD; - p->disk = disk; - if (register_cdrom(sbpcd_infop)) - { - printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n"); - } - disk->private_data = p; - disk->queue = sbpcd_queue; - add_disk(disk); - } - blk_queue_hardsect_size(sbpcd_queue, CD_FRAMESIZE); - -#ifndef MODULE - init_done: -#endif - return 0; -} -/*==========================================================================*/ -#ifdef MODULE -static void sbpcd_exit(void) -{ - int j; - - if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL)) - { - msg(DBG_INF, "What's that: can't unregister %s.\n", major_name); - return; - } - release_region(CDo_command,4); - blk_cleanup_queue(sbpcd_queue); - for (j=0;j<NR_SBPCD;j++) - { - if (D_S[j].drv_id==-1) continue; - del_gendisk(D_S[j].disk); - put_disk(D_S[j].disk); - vfree(D_S[j].sbp_buf); - if (D_S[j].sbp_audsiz>0) - vfree(D_S[j].aud_buf); - if ((unregister_cdrom(D_S[j].sbpcd_infop) == -EINVAL)) - { - msg(DBG_INF, "What's that: can't unregister info %s.\n", major_name); - return; - } - vfree(D_S[j].sbpcd_infop); - } - msg(DBG_INF, "%s module released.\n", major_name); -} - - -module_init(__sbpcd_init) /*HACK!*/; -module_exit(sbpcd_exit); - - -#endif /* MODULE */ -static int sbpcd_media_changed(struct cdrom_device_info *cdi, int disc_nr) -{ - struct sbpcd_drive *p = cdi->handle; - msg(DBG_CHK,"media_check (%s) called\n", cdi->name); - - if (p->CD_changed==0xFF) - { - p->CD_changed=0; - msg(DBG_CHK,"medium changed (drive %s)\n", cdi->name); - current_drive->diskstate_flags &= ~toc_bit; - /* we *don't* need invalidate here, it's done by caller */ - current_drive->diskstate_flags &= ~cd_size_bit; -#ifdef SAFE_MIXED - current_drive->has_data=0; -#endif /* SAFE_MIXED */ - - return (1); - } - else - return (0); -} - -MODULE_LICENSE("GPL"); -/* FIXME: Old modules.conf claims MATSUSHITA_CDROM2_MAJOR and CDROM3, but - AFAICT this doesn't support those majors, so why? --RR 30 Jul 2003 */ -MODULE_ALIAS_BLOCKDEV_MAJOR(MATSUSHITA_CDROM_MAJOR); - -/*==========================================================================*/ -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 8 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -8 - * c-argdecl-indent: 8 - * c-label-offset: -8 - * c-continued-statement-offset: 8 - * c-continued-brace-offset: 0 - * End: - */ - diff --git a/drivers/cdrom/sbpcd.h b/drivers/cdrom/sbpcd.h deleted file mode 100644 index 2f2225f13c6..00000000000 --- a/drivers/cdrom/sbpcd.h +++ /dev/null @@ -1,839 +0,0 @@ -/* - * sbpcd.h Specify interface address and interface type here. - */ - -/* - * Attention! This file contains user-serviceable parts! - * I recommend to make use of it... - * If you feel helpless, look into Documentation/cdrom/sbpcd - * (good idea anyway, at least before mailing me). - * - * The definitions for the first controller can get overridden by - * the kernel command line ("lilo boot option"). - * Examples: - * sbpcd=0x300,LaserMate - * or - * sbpcd=0x230,SoundBlaster - * or - * sbpcd=0x338,SoundScape - * or - * sbpcd=0x2C0,Teac16bit - * - * If sbpcd gets used as a module, you can load it with - * insmod sbpcd.o sbpcd=0x300,0 - * or - * insmod sbpcd.o sbpcd=0x230,1 - * or - * insmod sbpcd.o sbpcd=0x338,2 - * or - * insmod sbpcd.o sbpcd=0x2C0,3 - * respective to override the configured address and type. - */ - -/* - * define your CDROM port base address as CDROM_PORT - * and specify the type of your interface card as SBPRO. - * - * address: - * ======== - * SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ... - * LASERMATE type (CI-101P, WDH-7001C) addresses typically are 0x0300, ... - * SOUNDSCAPE addresses are from the LASERMATE type and range. You have to - * specify the REAL address here, not the configuration port address. Look - * at the CDROM driver's invoking line within your DOS CONFIG.SYS, or let - * sbpcd auto-probe, if you are not firm with the address. - * There are some soundcards on the market with 0x0630, 0x0650, ...; their - * type is not obvious (both types are possible). - * - * example: if your SBPRO audio address is 0x220, specify 0x230 and SBPRO 1. - * if your soundcard has its CDROM port above 0x300, specify - * that address and try SBPRO 0 first. - * if your SoundScape configuration port is at 0x330, specify - * 0x338 and SBPRO 2. - * - * interface type: - * =============== - * set SBPRO to 1 for "true" SoundBlaster card - * set SBPRO to 0 for "compatible" soundcards and - * for "poor" (no sound) interface cards. - * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards - * set SBPRO to 3 for Teac 16bit interface cards - * - * Almost all "compatible" sound boards need to set SBPRO to 0. - * If SBPRO is set wrong, the drives will get found - but any - * data access will give errors (audio access will work). - * The "OmniCD" no-sound interface card from CreativeLabs and most Teac - * interface cards need SBPRO 1. - * - * sound base: - * =========== - * The SOUND_BASE definition tells if we should try to turn the CD sound - * channels on. It will only be of use regarding soundcards with a SbPro - * compatible mixer. - * - * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels - * #define SOUND_BASE 0 leaves the soundcard untouched - */ -#define CDROM_PORT 0x340 /* <-----------<< port address */ -#define SBPRO 0 /* <-----------<< interface type */ -#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ -#define SOUND_BASE 0x220 /* <-----------<< sound address of this card or 0 */ - -/* - * some more or less user dependent definitions - service them! - */ - -/* Set this to 0 once you have configured your interface definitions right. */ -#define DISTRIBUTION 1 - -/* - * Time to wait after giving a message. - * This gets important if you enable non-standard DBG_xxx flags. - * You will see what happens if you omit the pause or make it - * too short. Be warned! - */ -#define KLOGD_PAUSE 1 - -/* tray control: eject tray if no disk is in */ -#if DISTRIBUTION -#define JUKEBOX 0 -#else -#define JUKEBOX 1 -#endif /* DISTRIBUTION */ - -/* tray control: eject tray after last use */ -#if DISTRIBUTION -#define EJECT 0 -#else -#define EJECT 1 -#endif /* DISTRIBUTION */ - -/* max. number of audio frames to read with one */ -/* request (allocates n* 2352 bytes kernel memory!) */ -/* may be freely adjusted, f.e. 75 (= 1 sec.), at */ -/* runtime by use of the CDROMAUDIOBUFSIZ ioctl. */ -#define READ_AUDIO 0 - -/* Optimizations for the Teac CD-55A drive read performance. - * SBP_TEAC_SPEED can be changed here, or one can set the - * variable "teac" when loading as a module. - * Valid settings are: - * 0 - very slow - the recommended "DISTRIBUTION 1" setup. - * 1 - 2x performance with little overhead. No busy waiting. - * 2 - 4x performance with 5ms overhead per read. Busy wait. - * - * Setting SBP_TEAC_SPEED or the variable 'teac' to anything - * other than 0 may cause problems. If you run into them, first - * change SBP_TEAC_SPEED back to 0 and see if your drive responds - * normally. If yes, you are "allowed" to report your case - to help - * me with the driver, not to solve your hassle. Don´t mail if you - * simply are stuck into your own "tuning" experiments, you know? - */ -#define SBP_TEAC_SPEED 1 - -/*==========================================================================*/ -/*==========================================================================*/ -/* - * nothing to change below here if you are not fully aware what you're doing - */ -#ifndef _LINUX_SBPCD_H - -#define _LINUX_SBPCD_H -/*==========================================================================*/ -/*==========================================================================*/ -/* - * driver's own read_ahead, data mode - */ -#define SBP_BUFFER_FRAMES 8 - -#define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */ -#undef FUTURE -#undef SAFE_MIXED - -#define TEST_UPC 0 -#define SPEA_TEST 0 -#define TEST_STI 0 -#define OLD_BUSY 0 -#undef PATH_CHECK -#ifndef SOUND_BASE -#define SOUND_BASE 0 -#endif -#if DISTRIBUTION -#undef SBP_TEAC_SPEED -#define SBP_TEAC_SPEED 0 -#endif -/*==========================================================================*/ -/* - * DDI interface definitions - * "invented" by Fred N. van Kempen.. - */ -#define DDIOCSDBG 0x9000 - -/*==========================================================================*/ -/* - * "private" IOCTL functions - */ -#define CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */ - -/*==========================================================================*/ -/* - * Debug output levels - */ -#define DBG_INF 1 /* necessary information */ -#define DBG_BSZ 2 /* BLOCK_SIZE trace */ -#define DBG_REA 3 /* READ status trace */ -#define DBG_CHK 4 /* MEDIA CHECK trace */ -#define DBG_TIM 5 /* datarate timer test */ -#define DBG_INI 6 /* initialization trace */ -#define DBG_TOC 7 /* tell TocEntry values */ -#define DBG_IOC 8 /* ioctl trace */ -#define DBG_STA 9 /* ResponseStatus() trace */ -#define DBG_ERR 10 /* cc_ReadError() trace */ -#define DBG_CMD 11 /* cmd_out() trace */ -#define DBG_WRN 12 /* give explanation before auto-probing */ -#define DBG_MUL 13 /* multi session code test */ -#define DBG_IDX 14 /* test code for drive_id !=0 */ -#define DBG_IOX 15 /* some special information */ -#define DBG_DID 16 /* drive ID test */ -#define DBG_RES 17 /* drive reset info */ -#define DBG_SPI 18 /* SpinUp test */ -#define DBG_IOS 19 /* ioctl trace: subchannel functions */ -#define DBG_IO2 20 /* ioctl trace: general */ -#define DBG_UPC 21 /* show UPC information */ -#define DBG_XA1 22 /* XA mode debugging */ -#define DBG_LCK 23 /* door (un)lock info */ -#define DBG_SQ1 24 /* dump SubQ frame */ -#define DBG_AUD 25 /* READ AUDIO debugging */ -#define DBG_SEQ 26 /* Sequoia interface configuration trace */ -#define DBG_LCS 27 /* Longshine LCS-7260 debugging trace */ -#define DBG_CD2 28 /* MKE/Funai CD200 debugging trace */ -#define DBG_TEA 29 /* TEAC CD-55A debugging trace */ -#define DBG_ECS 30 /* ECS-AT (Vertos 100) debugging trace */ -#define DBG_000 31 /* unnecessary information */ - -/*==========================================================================*/ -/*==========================================================================*/ - -/* - * bits of flags_cmd_out: - */ -#define f_respo3 0x100 -#define f_putcmd 0x80 -#define f_respo2 0x40 -#define f_lopsta 0x20 -#define f_getsta 0x10 -#define f_ResponseStatus 0x08 -#define f_obey_p_check 0x04 -#define f_bit1 0x02 -#define f_wait_if_busy 0x01 - -/* - * diskstate_flags: - */ -#define x80_bit 0x80 -#define upc_bit 0x40 -#define volume_bit 0x20 -#define toc_bit 0x10 -#define multisession_bit 0x08 -#define cd_size_bit 0x04 -#define subq_bit 0x02 -#define frame_size_bit 0x01 - -/* - * disk states (bits of diskstate_flags): - */ -#define upc_valid (current_drive->diskstate_flags&upc_bit) -#define volume_valid (current_drive->diskstate_flags&volume_bit) -#define toc_valid (current_drive->diskstate_flags&toc_bit) -#define cd_size_valid (current_drive->diskstate_flags&cd_size_bit) -#define subq_valid (current_drive->diskstate_flags&subq_bit) -#define frame_size_valid (current_drive->diskstate_flags&frame_size_bit) - -/* - * the status_bits variable - */ -#define p_success 0x100 -#define p_door_closed 0x80 -#define p_caddy_in 0x40 -#define p_spinning 0x20 -#define p_check 0x10 -#define p_busy_new 0x08 -#define p_door_locked 0x04 -#define p_disk_ok 0x01 - -/* - * LCS-7260 special status result bits: - */ -#define p_lcs_door_locked 0x02 -#define p_lcs_door_closed 0x01 /* probably disk_in */ - -/* - * CR-52x special status result bits: - */ -#define p_caddin_old 0x40 -#define p_success_old 0x08 -#define p_busy_old 0x04 -#define p_bit_1 0x02 /* hopefully unused now */ - -/* - * "generation specific" defs of the status result bits: - */ -#define p0_door_closed 0x80 -#define p0_caddy_in 0x40 -#define p0_spinning 0x20 -#define p0_check 0x10 -#define p0_success 0x08 /* unused */ -#define p0_busy 0x04 -#define p0_bit_1 0x02 /* unused */ -#define p0_disk_ok 0x01 - -#define pL_disk_in 0x40 -#define pL_spinning 0x20 -#define pL_check 0x10 -#define pL_success 0x08 /* unused ?? */ -#define pL_busy 0x04 -#define pL_door_locked 0x02 -#define pL_door_closed 0x01 - -#define pV_door_closed 0x40 -#define pV_spinning 0x20 -#define pV_check 0x10 -#define pV_success 0x08 -#define pV_busy 0x04 -#define pV_door_locked 0x02 -#define pV_disk_ok 0x01 - -#define p1_door_closed 0x80 -#define p1_disk_in 0x40 -#define p1_spinning 0x20 -#define p1_check 0x10 -#define p1_busy 0x08 -#define p1_door_locked 0x04 -#define p1_bit_1 0x02 /* unused */ -#define p1_disk_ok 0x01 - -#define p2_disk_ok 0x80 -#define p2_door_locked 0x40 -#define p2_spinning 0x20 -#define p2_busy2 0x10 -#define p2_busy1 0x08 -#define p2_door_closed 0x04 -#define p2_disk_in 0x02 -#define p2_check 0x01 - -/* - * used drive states: - */ -#define st_door_closed (current_drive->status_bits&p_door_closed) -#define st_caddy_in (current_drive->status_bits&p_caddy_in) -#define st_spinning (current_drive->status_bits&p_spinning) -#define st_check (current_drive->status_bits&p_check) -#define st_busy (current_drive->status_bits&p_busy_new) -#define st_door_locked (current_drive->status_bits&p_door_locked) -#define st_diskok (current_drive->status_bits&p_disk_ok) - -/* - * bits of the CDi_status register: - */ -#define s_not_result_ready 0x04 /* 0: "result ready" */ -#define s_not_data_ready 0x02 /* 0: "data ready" */ -#define s_attention 0x01 /* 1: "attention required" */ -/* - * usable as: - */ -#define DRV_ATTN ((inb(CDi_status)&s_attention)!=0) -#define DATA_READY ((inb(CDi_status)&s_not_data_ready)==0) -#define RESULT_READY ((inb(CDi_status)&s_not_result_ready)==0) - -/* - * drive families and types (firmware versions): - */ -#define drv_fam0 0x0100 /* CR-52x family */ -#define drv_199 (drv_fam0+0x01) /* <200 */ -#define drv_200 (drv_fam0+0x02) /* <201 */ -#define drv_201 (drv_fam0+0x03) /* <210 */ -#define drv_210 (drv_fam0+0x04) /* <211 */ -#define drv_211 (drv_fam0+0x05) /* <300 */ -#define drv_300 (drv_fam0+0x06) /* >=300 */ - -#define drv_fam1 0x0200 /* CR-56x family */ -#define drv_099 (drv_fam1+0x01) /* <100 */ -#define drv_100 (drv_fam1+0x02) /* >=100, only 1.02 and 5.00 known */ - -#define drv_fam2 0x0400 /* CD200 family */ - -#define drv_famT 0x0800 /* TEAC CD-55A */ - -#define drv_famL 0x1000 /* Longshine family */ -#define drv_260 (drv_famL+0x01) /* LCS-7260 */ -#define drv_e1 (drv_famL+0x01) /* LCS-7260, firmware "A E1" */ -#define drv_f4 (drv_famL+0x02) /* LCS-7260, firmware "A4F4" */ - -#define drv_famV 0x2000 /* ECS-AT (vertos-100) family */ -#define drv_at (drv_famV+0x01) /* ECS-AT, firmware "1.00" */ - -#define fam0_drive (current_drive->drv_type&drv_fam0) -#define famL_drive (current_drive->drv_type&drv_famL) -#define famV_drive (current_drive->drv_type&drv_famV) -#define fam1_drive (current_drive->drv_type&drv_fam1) -#define fam2_drive (current_drive->drv_type&drv_fam2) -#define famT_drive (current_drive->drv_type&drv_famT) -#define fam0L_drive (current_drive->drv_type&(drv_fam0|drv_famL)) -#define fam0V_drive (current_drive->drv_type&(drv_fam0|drv_famV)) -#define famLV_drive (current_drive->drv_type&(drv_famL|drv_famV)) -#define fam0LV_drive (current_drive->drv_type&(drv_fam0|drv_famL|drv_famV)) -#define fam1L_drive (current_drive->drv_type&(drv_fam1|drv_famL)) -#define fam1V_drive (current_drive->drv_type&(drv_fam1|drv_famV)) -#define fam1LV_drive (current_drive->drv_type&(drv_fam1|drv_famL|drv_famV)) -#define fam01_drive (current_drive->drv_type&(drv_fam0|drv_fam1)) -#define fam12_drive (current_drive->drv_type&(drv_fam1|drv_fam2)) -#define fam2T_drive (current_drive->drv_type&(drv_fam2|drv_famT)) - -/* - * audio states: - */ -#define audio_completed 3 /* Forgot this one! --AJK */ -#define audio_playing 2 -#define audio_pausing 1 - -/* - * drv_pattern, drv_options: - */ -#define speed_auto 0x80 -#define speed_300 0x40 -#define speed_150 0x20 -#define audio_mono 0x04 - -/* - * values of cmd_type (0 else): - */ -#define READ_M1 0x01 /* "data mode 1": 2048 bytes per frame */ -#define READ_M2 0x02 /* "data mode 2": 12+2048+280 bytes per frame */ -#define READ_SC 0x04 /* "subchannel info": 96 bytes per frame */ -#define READ_AU 0x08 /* "audio frame": 2352 bytes per frame */ - -/* - * sense_byte: - * - * values: 00 - * 01 - * 81 - * 82 "raw audio" mode - * xx from infobuf[0] after 85 00 00 00 00 00 00 - */ - -/* audio status (bin) */ -#define aud_00 0x00 /* Audio status byte not supported or not valid */ -#define audx11 0x0b /* Audio play operation in progress */ -#define audx12 0x0c /* Audio play operation paused */ -#define audx13 0x0d /* Audio play operation successfully completed */ -#define audx14 0x0e /* Audio play operation stopped due to error */ -#define audx15 0x0f /* No current audio status to return */ -/* audio status (bcd) */ -#define aud_11 0x11 /* Audio play operation in progress */ -#define aud_12 0x12 /* Audio play operation paused */ -#define aud_13 0x13 /* Audio play operation successfully completed */ -#define aud_14 0x14 /* Audio play operation stopped due to error */ -#define aud_15 0x15 /* No current audio status to return */ - -/* - * highest allowed drive number (MINOR+1) - */ -#define NR_SBPCD 4 - -/* - * we try to never disable interrupts - seems to work - */ -#define SBPCD_DIS_IRQ 0 - -/* - * "write byte to port" - */ -#define OUT(x,y) outb(y,x) - -/*==========================================================================*/ - -#define MIXER_addr SOUND_BASE+4 /* sound card's address register */ -#define MIXER_data SOUND_BASE+5 /* sound card's data register */ -#define MIXER_CD_Volume 0x28 /* internal SB Pro register address */ - -/*==========================================================================*/ - -#define MAX_TRACKS 99 - -#define ERR_DISKCHANGE 615 - -/*==========================================================================*/ -/* - * To make conversions easier (machine dependent!) - */ -typedef union _msf -{ - u_int n; - u_char c[4]; -} MSF; - -typedef union _blk -{ - u_int n; - u_char c[4]; -} BLK; - -/*==========================================================================*/ - -/*============================================================================ -============================================================================== - -COMMAND SET of "old" drives like CR-521, CR-522 - (the CR-562 family is different): - -No. Command Code --------------------------------------------- - -Drive Commands: - 1 Seek 01 - 2 Read Data 02 - 3 Read XA-Data 03 - 4 Read Header 04 - 5 Spin Up 05 - 6 Spin Down 06 - 7 Diagnostic 07 - 8 Read UPC 08 - 9 Read ISRC 09 -10 Play Audio 0A -11 Play Audio MSF 0B -12 Play Audio Track/Index 0C - -Status Commands: -13 Read Status 81 -14 Read Error 82 -15 Read Drive Version 83 -16 Mode Select 84 -17 Mode Sense 85 -18 Set XA Parameter 86 -19 Read XA Parameter 87 -20 Read Capacity 88 -21 Read SUB_Q 89 -22 Read Disc Code 8A -23 Read Disc Information 8B -24 Read TOC 8C -25 Pause/Resume 8D -26 Read Packet 8E -27 Read Path Check 00 - - -all numbers (lba, msf-bin, msf-bcd, counts) to transfer high byte first - -mnemo 7-byte command #bytes response (r0...rn) -________ ____________________ ____ - -Read Status: -status: 81. (1) one-byte command, gives the main - status byte -Read Error: -check1: 82 00 00 00 00 00 00. (6) r1: audio status - -Read Packet: -check2: 8e xx 00 00 00 00 00. (xx) gets xx bytes response, relating - to commands 01 04 05 07 08 09 - -Play Audio: -play: 0a ll-bb-aa nn-nn-nn. (0) play audio, ll-bb-aa: starting block (lba), - nn-nn-nn: #blocks -Play Audio MSF: - 0b mm-ss-ff mm-ss-ff (0) play audio from/to - -Play Audio Track/Index: - 0c ... - -Pause/Resume: -pause: 8d pr 00 00 00 00 00. (0) pause (pr=00) - resume (pr=80) audio playing - -Mode Select: - 84 00 nn-nn ??.?? 00 (0) nn-nn: 2048 or 2340 - possibly defines transfer size - -set_vol: 84 83 00 00 sw le 00. (0) sw(itch): lrxxxxxx (off=1) - le(vel): min=0, max=FF, else half - (firmware 2.11) - -Mode Sense: -get_vol: 85 03 00 00 00 00 00. (2) tell current audio volume setting - -Read Disc Information: -tocdesc: 8b 00 00 00 00 00 00. (6) read the toc descriptor ("msf-bin"-format) - -Read TOC: -tocent: 8c fl nn 00 00 00 00. (8) read toc entry #nn - (fl=0:"lba"-, =2:"msf-bin"-format) - -Read Capacity: -capacit: 88 00 00 00 00 00 00. (5) "read CD-ROM capacity" - - -Read Path Check: -ping: 00 00 00 00 00 00 00. (2) r0=AA, r1=55 - ("ping" if the drive is connected) - -Read Drive Version: -ident: 83 00 00 00 00 00 00. (12) gives "MATSHITAn.nn" - (n.nn = 2.01, 2.11., 3.00, ...) - -Seek: -seek: 01 00 ll-bb-aa 00 00. (0) -seek: 01 02 mm-ss-ff 00 00. (0) - -Read Data: -read: 02 xx-xx-xx nn-nn fl. (?) read nn-nn blocks of 2048 bytes, - starting at block xx-xx-xx - fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx - -Read XA-Data: -read: 03 xx-xx-xx nn-nn fl. (?) read nn-nn blocks of 2340 bytes, - starting at block xx-xx-xx - fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx - -Read SUB_Q: - 89 fl 00 00 00 00 00. (13) r0: audio status, r4-r7: lba/msf, - fl=0: "lba", fl=2: "msf" - -Read Disc Code: - 8a 00 00 00 00 00 00. (14) possibly extended "check condition"-info - -Read Header: - 04 00 ll-bb-aa 00 00. (0) 4 bytes response with "check2" - 04 02 mm-ss-ff 00 00. (0) 4 bytes response with "check2" - -Spin Up: - 05 00 ll-bb-aa 00 00. (0) possibly implies a "seek" - -Spin Down: - 06 ... - -Diagnostic: - 07 00 ll-bb-aa 00 00. (2) 2 bytes response with "check2" - 07 02 mm-ss-ff 00 00. (2) 2 bytes response with "check2" - -Read UPC: - 08 00 ll-bb-aa 00 00. (16) - 08 02 mm-ss-ff 00 00. (16) - -Read ISRC: - 09 00 ll-bb-aa 00 00. (15) 15 bytes response with "check2" - 09 02 mm-ss-ff 00 00. (15) 15 bytes response with "check2" - -Set XA Parameter: - 86 ... - -Read XA Parameter: - 87 ... - -============================================================================== -============================================================================*/ - -/* - * commands - * - * CR-52x: CMD0_ - * CR-56x: CMD1_ - * CD200: CMD2_ - * LCS-7260: CMDL_ - * TEAC CD-55A: CMDT_ - * ECS-AT: CMDV_ - */ -#define CMD1_RESET 0x0a -#define CMD2_RESET 0x01 -#define CMDT_RESET 0xc0 - -#define CMD1_LOCK_CTL 0x0c -#define CMD2_LOCK_CTL 0x1e -#define CMDT_LOCK_CTL CMD2_LOCK_CTL -#define CMDL_LOCK_CTL 0x0e -#define CMDV_LOCK_CTL CMDL_LOCK_CTL - -#define CMD1_TRAY_CTL 0x07 -#define CMD2_TRAY_CTL 0x1b -#define CMDT_TRAY_CTL CMD2_TRAY_CTL -#define CMDL_TRAY_CTL 0x0d -#define CMDV_TRAY_CTL CMDL_TRAY_CTL - -#define CMD1_MULTISESS 0x8d -#define CMDL_MULTISESS 0x8c -#define CMDV_MULTISESS CMDL_MULTISESS - -#define CMD1_SUBCHANINF 0x11 -#define CMD2_SUBCHANINF 0x?? - -#define CMD1_ABORT 0x08 -#define CMD2_ABORT 0x08 -#define CMDT_ABORT 0x08 - -#define CMD2_x02 0x02 - -#define CMD2_SETSPEED 0xda - -#define CMD0_PATH_CHECK 0x00 -#define CMD1_PATH_CHECK 0x??? -#define CMD2_PATH_CHECK 0x??? -#define CMDT_PATH_CHECK 0x??? -#define CMDL_PATH_CHECK CMD0_PATH_CHECK -#define CMDV_PATH_CHECK CMD0_PATH_CHECK - -#define CMD0_SEEK 0x01 -#define CMD1_SEEK CMD0_SEEK -#define CMD2_SEEK 0x2b -#define CMDT_SEEK CMD2_SEEK -#define CMDL_SEEK CMD0_SEEK -#define CMDV_SEEK CMD0_SEEK - -#define CMD0_READ 0x02 -#define CMD1_READ 0x10 -#define CMD2_READ 0x28 -#define CMDT_READ CMD2_READ -#define CMDL_READ CMD0_READ -#define CMDV_READ CMD0_READ - -#define CMD0_READ_XA 0x03 -#define CMD2_READ_XA 0xd4 -#define CMD2_READ_XA2 0xd5 -#define CMDL_READ_XA CMD0_READ_XA /* really ?? */ -#define CMDV_READ_XA CMD0_READ_XA - -#define CMD0_READ_HEAD 0x04 - -#define CMD0_SPINUP 0x05 -#define CMD1_SPINUP 0x02 -#define CMD2_SPINUP CMD2_TRAY_CTL -#define CMDL_SPINUP CMD0_SPINUP -#define CMDV_SPINUP CMD0_SPINUP - -#define CMD0_SPINDOWN 0x06 /* really??? */ -#define CMD1_SPINDOWN 0x06 -#define CMD2_SPINDOWN CMD2_TRAY_CTL -#define CMDL_SPINDOWN 0x0d -#define CMDV_SPINDOWN CMD0_SPINDOWN - -#define CMD0_DIAG 0x07 - -#define CMD0_READ_UPC 0x08 -#define CMD1_READ_UPC 0x88 -#define CMD2_READ_UPC 0x??? -#define CMDL_READ_UPC CMD0_READ_UPC -#define CMDV_READ_UPC 0x8f - -#define CMD0_READ_ISRC 0x09 - -#define CMD0_PLAY 0x0a -#define CMD1_PLAY 0x??? -#define CMD2_PLAY 0x??? -#define CMDL_PLAY CMD0_PLAY -#define CMDV_PLAY CMD0_PLAY - -#define CMD0_PLAY_MSF 0x0b -#define CMD1_PLAY_MSF 0x0e -#define CMD2_PLAY_MSF 0x47 -#define CMDT_PLAY_MSF CMD2_PLAY_MSF -#define CMDL_PLAY_MSF 0x??? - -#define CMD0_PLAY_TI 0x0c -#define CMD1_PLAY_TI 0x0f - -#define CMD0_STATUS 0x81 -#define CMD1_STATUS 0x05 -#define CMD2_STATUS 0x00 -#define CMDT_STATUS CMD2_STATUS -#define CMDL_STATUS CMD0_STATUS -#define CMDV_STATUS CMD0_STATUS -#define CMD2_SEEK_LEADIN 0x00 - -#define CMD0_READ_ERR 0x82 -#define CMD1_READ_ERR CMD0_READ_ERR -#define CMD2_READ_ERR 0x03 -#define CMDT_READ_ERR CMD2_READ_ERR /* get audio status */ -#define CMDL_READ_ERR CMD0_READ_ERR -#define CMDV_READ_ERR CMD0_READ_ERR - -#define CMD0_READ_VER 0x83 -#define CMD1_READ_VER CMD0_READ_VER -#define CMD2_READ_VER 0x12 -#define CMDT_READ_VER CMD2_READ_VER /* really ?? */ -#define CMDL_READ_VER CMD0_READ_VER -#define CMDV_READ_VER CMD0_READ_VER - -#define CMD0_SETMODE 0x84 -#define CMD1_SETMODE 0x09 -#define CMD2_SETMODE 0x55 -#define CMDT_SETMODE CMD2_SETMODE -#define CMDL_SETMODE CMD0_SETMODE - -#define CMD0_GETMODE 0x85 -#define CMD1_GETMODE 0x84 -#define CMD2_GETMODE 0x5a -#define CMDT_GETMODE CMD2_GETMODE -#define CMDL_GETMODE CMD0_GETMODE - -#define CMD0_SET_XA 0x86 - -#define CMD0_GET_XA 0x87 - -#define CMD0_CAPACITY 0x88 -#define CMD1_CAPACITY 0x85 -#define CMD2_CAPACITY 0x25 -#define CMDL_CAPACITY CMD0_CAPACITY /* missing in some firmware versions */ - -#define CMD0_READSUBQ 0x89 -#define CMD1_READSUBQ 0x87 -#define CMD2_READSUBQ 0x42 -#define CMDT_READSUBQ CMD2_READSUBQ -#define CMDL_READSUBQ CMD0_READSUBQ -#define CMDV_READSUBQ CMD0_READSUBQ - -#define CMD0_DISKCODE 0x8a - -#define CMD0_DISKINFO 0x8b -#define CMD1_DISKINFO CMD0_DISKINFO -#define CMD2_DISKINFO 0x43 -#define CMDT_DISKINFO CMD2_DISKINFO -#define CMDL_DISKINFO CMD0_DISKINFO -#define CMDV_DISKINFO CMD0_DISKINFO - -#define CMD0_READTOC 0x8c -#define CMD1_READTOC CMD0_READTOC -#define CMD2_READTOC 0x??? -#define CMDL_READTOC CMD0_READTOC -#define CMDV_READTOC CMD0_READTOC - -#define CMD0_PAU_RES 0x8d -#define CMD1_PAU_RES 0x0d -#define CMD2_PAU_RES 0x4b -#define CMDT_PAUSE CMD2_PAU_RES -#define CMDL_PAU_RES CMD0_PAU_RES -#define CMDV_PAUSE CMD0_PAU_RES - -#define CMD0_PACKET 0x8e -#define CMD1_PACKET CMD0_PACKET -#define CMD2_PACKET 0x??? -#define CMDL_PACKET CMD0_PACKET -#define CMDV_PACKET 0x??? - -/*==========================================================================*/ -/*==========================================================================*/ -#endif /* _LINUX_SBPCD_H */ -/*==========================================================================*/ -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 8 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -8 - * c-argdecl-indent: 8 - * c-label-offset: -8 - * c-continued-statement-offset: 8 - * c-continued-brace-offset: 0 - * End: - */ diff --git a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c deleted file mode 100644 index 5409fca5bbf..00000000000 --- a/drivers/cdrom/sjcd.c +++ /dev/null @@ -1,1815 +0,0 @@ -/* -- sjcd.c - * - * Sanyo CD-ROM device driver implementation, Version 1.6 - * Copyright (C) 1995 Vadim V. Model - * - * model@cecmow.enet.dec.com - * vadim@rbrf.ru - * vadim@ipsun.ras.ru - * - * - * This driver is based on pre-works by Eberhard Moenkeberg (emoenke@gwdg.de); - * it was developed under use of mcd.c from Martin Harriss, with help of - * Eric van der Maarel (H.T.M.v.d.Maarel@marin.nl). - * - * It is planned to include these routines into sbpcd.c later - to make - * a "mixed use" on one cable possible for all kinds of drives which use - * the SoundBlaster/Panasonic style CDROM interface. But today, the - * ability to install directly from CDROM is more important than flexibility. - * - * 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. - * - * History: - * 1.1 First public release with kernel version 1.3.7. - * Written by Vadim Model. - * 1.2 Added detection and configuration of cdrom interface - * on ISP16 soundcard. - * Allow for command line options: sjcd=<io_base>,<irq>,<dma> - * 1.3 Some minor changes to README.sjcd. - * 1.4 MSS Sound support!! Listen to a CD through the speakers. - * 1.5 Module support and bugfixes. - * Tray locking. - * 1.6 Removed ISP16 code from this driver. - * Allow only to set io base address on command line: sjcd=<io_base> - * Changes to Documentation/cdrom/sjcd - * Added cleanup after any error in the initialisation. - * 1.7 Added code to set the sector size tables to prevent the bug present in - * the previous version of this driver. Coded added by Anthony Barbachan - * from bugfix tip originally suggested by Alan Cox. - * - * November 1999 -- Make kernel-parameter implementation work with 2.3.x - * Removed init_module & cleanup_module in favor of - * module_init & module_exit. - * Torben Mathiasen <tmm@image.dk> - */ - -#define SJCD_VERSION_MAJOR 1 -#define SJCD_VERSION_MINOR 7 - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/mm.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/cdrom.h> -#include <linux/ioport.h> -#include <linux/string.h> -#include <linux/major.h> -#include <linux/init.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <linux/blkdev.h> -#include "sjcd.h" - -static int sjcd_present = 0; -static struct request_queue *sjcd_queue; - -#define MAJOR_NR SANYO_CDROM_MAJOR -#define QUEUE (sjcd_queue) -#define CURRENT elv_next_request(sjcd_queue) - -#define SJCD_BUF_SIZ 32 /* cdr-h94a has internal 64K buffer */ - -/* - * buffer for block size conversion - */ -static char sjcd_buf[2048 * SJCD_BUF_SIZ]; -static volatile int sjcd_buf_bn[SJCD_BUF_SIZ], sjcd_next_bn; -static volatile int sjcd_buf_in, sjcd_buf_out = -1; - -/* - * Status. - */ -static unsigned short sjcd_status_valid = 0; -static unsigned short sjcd_door_closed; -static unsigned short sjcd_door_was_open; -static unsigned short sjcd_media_is_available; -static unsigned short sjcd_media_is_changed; -static unsigned short sjcd_toc_uptodate = 0; -static unsigned short sjcd_command_failed; -static volatile unsigned char sjcd_completion_status = 0; -static volatile unsigned char sjcd_completion_error = 0; -static unsigned short sjcd_command_is_in_progress = 0; -static unsigned short sjcd_error_reported = 0; -static DEFINE_SPINLOCK(sjcd_lock); - -static int sjcd_open_count; - -static int sjcd_audio_status; -static struct sjcd_play_msf sjcd_playing; - -static int sjcd_base = SJCD_BASE_ADDR; - -module_param(sjcd_base, int, 0); - -static DECLARE_WAIT_QUEUE_HEAD(sjcd_waitq); - -/* - * Data transfer. - */ -static volatile unsigned short sjcd_transfer_is_active = 0; - -enum sjcd_transfer_state { - SJCD_S_IDLE = 0, - SJCD_S_START = 1, - SJCD_S_MODE = 2, - SJCD_S_READ = 3, - SJCD_S_DATA = 4, - SJCD_S_STOP = 5, - SJCD_S_STOPPING = 6 -}; -static enum sjcd_transfer_state sjcd_transfer_state = SJCD_S_IDLE; -static long sjcd_transfer_timeout = 0; -static int sjcd_read_count = 0; -static unsigned char sjcd_mode = 0; - -#define SJCD_READ_TIMEOUT 5000 - -#if defined( SJCD_GATHER_STAT ) -/* - * Statistic. - */ -static struct sjcd_stat statistic; -#endif - -/* - * Timer. - */ -static DEFINE_TIMER(sjcd_delay_timer, NULL, 0, 0); - -#define SJCD_SET_TIMER( func, tmout ) \ - ( sjcd_delay_timer.expires = jiffies+tmout, \ - sjcd_delay_timer.function = ( void * )func, \ - add_timer( &sjcd_delay_timer ) ) - -#define CLEAR_TIMER del_timer( &sjcd_delay_timer ) - -/* - * Set up device, i.e., use command line data to set - * base address. - */ -#ifndef MODULE -static int __init sjcd_setup(char *str) -{ - int ints[2]; - (void) get_options(str, ARRAY_SIZE(ints), ints); - if (ints[0] > 0) - sjcd_base = ints[1]; - - return 1; -} - -__setup("sjcd=", sjcd_setup); - -#endif - -/* - * Special converters. - */ -static unsigned char bin2bcd(int bin) -{ - int u, v; - - u = bin % 10; - v = bin / 10; - return (u | (v << 4)); -} - -static int bcd2bin(unsigned char bcd) -{ - return ((bcd >> 4) * 10 + (bcd & 0x0F)); -} - -static long msf2hsg(struct msf *mp) -{ - return (bcd2bin(mp->frame) + bcd2bin(mp->sec) * 75 - + bcd2bin(mp->min) * 4500 - 150); -} - -static void hsg2msf(long hsg, struct msf *msf) -{ - hsg += 150; - msf->min = hsg / 4500; - hsg %= 4500; - msf->sec = hsg / 75; - msf->frame = hsg % 75; - msf->min = bin2bcd(msf->min); /* convert to BCD */ - msf->sec = bin2bcd(msf->sec); - msf->frame = bin2bcd(msf->frame); -} - -/* - * Send a command to cdrom. Invalidate status. - */ -static void sjcd_send_cmd(unsigned char cmd) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: send_cmd( 0x%x )\n", cmd); -#endif - outb(cmd, SJCDPORT(0)); - sjcd_command_is_in_progress = 1; - sjcd_status_valid = 0; - sjcd_command_failed = 0; -} - -/* - * Send a command with one arg to cdrom. Invalidate status. - */ -static void sjcd_send_1_cmd(unsigned char cmd, unsigned char a) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: send_1_cmd( 0x%x, 0x%x )\n", cmd, a); -#endif - outb(cmd, SJCDPORT(0)); - outb(a, SJCDPORT(0)); - sjcd_command_is_in_progress = 1; - sjcd_status_valid = 0; - sjcd_command_failed = 0; -} - -/* - * Send a command with four args to cdrom. Invalidate status. - */ -static void sjcd_send_4_cmd(unsigned char cmd, unsigned char a, - unsigned char b, unsigned char c, - unsigned char d) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: send_4_cmd( 0x%x )\n", cmd); -#endif - outb(cmd, SJCDPORT(0)); - outb(a, SJCDPORT(0)); - outb(b, SJCDPORT(0)); - outb(c, SJCDPORT(0)); - outb(d, SJCDPORT(0)); - sjcd_command_is_in_progress = 1; - sjcd_status_valid = 0; - sjcd_command_failed = 0; -} - -/* - * Send a play or read command to cdrom. Invalidate Status. - */ -static void sjcd_send_6_cmd(unsigned char cmd, struct sjcd_play_msf *pms) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: send_long_cmd( 0x%x )\n", cmd); -#endif - outb(cmd, SJCDPORT(0)); - outb(pms->start.min, SJCDPORT(0)); - outb(pms->start.sec, SJCDPORT(0)); - outb(pms->start.frame, SJCDPORT(0)); - outb(pms->end.min, SJCDPORT(0)); - outb(pms->end.sec, SJCDPORT(0)); - outb(pms->end.frame, SJCDPORT(0)); - sjcd_command_is_in_progress = 1; - sjcd_status_valid = 0; - sjcd_command_failed = 0; -} - -/* - * Get a value from the data port. Should not block, so we use a little - * wait for a while. Returns 0 if OK. - */ -static int sjcd_load_response(void *buf, int len) -{ - unsigned char *resp = (unsigned char *) buf; - - for (; len; --len) { - int i; - for (i = 200; - i-- && !SJCD_STATUS_AVAILABLE(inb(SJCDPORT(1)));); - if (i > 0) - *resp++ = (unsigned char) inb(SJCDPORT(0)); - else - break; - } - return (len); -} - -/* - * Load and parse command completion status (drive info byte and maybe error). - * Sorry, no error classification yet. - */ -static void sjcd_load_status(void) -{ - sjcd_media_is_changed = 0; - sjcd_completion_error = 0; - sjcd_completion_status = inb(SJCDPORT(0)); - if (sjcd_completion_status & SST_DOOR_OPENED) { - sjcd_door_closed = sjcd_media_is_available = 0; - } else { - sjcd_door_closed = 1; - if (sjcd_completion_status & SST_MEDIA_CHANGED) - sjcd_media_is_available = sjcd_media_is_changed = - 1; - else if (sjcd_completion_status & 0x0F) { - /* - * OK, we seem to catch an error ... - */ - while (!SJCD_STATUS_AVAILABLE(inb(SJCDPORT(1)))); - sjcd_completion_error = inb(SJCDPORT(0)); - if ((sjcd_completion_status & 0x08) && - (sjcd_completion_error & 0x40)) - sjcd_media_is_available = 0; - else - sjcd_command_failed = 1; - } else - sjcd_media_is_available = 1; - } - /* - * Ok, status loaded successfully. - */ - sjcd_status_valid = 1, sjcd_error_reported = 0; - sjcd_command_is_in_progress = 0; - - /* - * If the disk is changed, the TOC is not valid. - */ - if (sjcd_media_is_changed) - sjcd_toc_uptodate = 0; -#if defined( SJCD_TRACE ) - printk("SJCD: status %02x.%02x loaded.\n", - (int) sjcd_completion_status, (int) sjcd_completion_error); -#endif -} - -/* - * Read status from cdrom. Check to see if the status is available. - */ -static int sjcd_check_status(void) -{ - /* - * Try to load the response from cdrom into buffer. - */ - if (SJCD_STATUS_AVAILABLE(inb(SJCDPORT(1)))) { - sjcd_load_status(); - return (1); - } else { - /* - * No status is available. - */ - return (0); - } -} - -/* - * This is just timeout counter, and nothing more. Surprised ? :-) - */ -static volatile long sjcd_status_timeout; - -/* - * We need about 10 seconds to wait. The longest command takes about 5 seconds - * to probe the disk (usually after tray closed or drive reset). Other values - * should be thought of for other commands. - */ -#define SJCD_WAIT_FOR_STATUS_TIMEOUT 1000 - -static void sjcd_status_timer(void) -{ - if (sjcd_check_status()) { - /* - * The command completed and status is loaded, stop waiting. - */ - wake_up(&sjcd_waitq); - } else if (--sjcd_status_timeout <= 0) { - /* - * We are timed out. - */ - wake_up(&sjcd_waitq); - } else { - /* - * We have still some time to wait. Try again. - */ - SJCD_SET_TIMER(sjcd_status_timer, 1); - } -} - -/* - * Wait for status for 10 sec approx. Returns non-positive when timed out. - * Should not be used while reading data CDs. - */ -static int sjcd_wait_for_status(void) -{ - sjcd_status_timeout = SJCD_WAIT_FOR_STATUS_TIMEOUT; - SJCD_SET_TIMER(sjcd_status_timer, 1); - sleep_on(&sjcd_waitq); -#if defined( SJCD_DIAGNOSTIC ) || defined ( SJCD_TRACE ) - if (sjcd_status_timeout <= 0) - printk("SJCD: Error Wait For Status.\n"); -#endif - return (sjcd_status_timeout); -} - -static int sjcd_receive_status(void) -{ - int i; -#if defined( SJCD_TRACE ) - printk("SJCD: receive_status\n"); -#endif - /* - * Wait a bit for status available. - */ - for (i = 200; i-- && (sjcd_check_status() == 0);); - if (i < 0) { -#if defined( SJCD_TRACE ) - printk("SJCD: long wait for status\n"); -#endif - if (sjcd_wait_for_status() <= 0) - printk("SJCD: Timeout when read status.\n"); - else - i = 0; - } - return (i); -} - -/* - * Load the status. Issue get status command and wait for status available. - */ -static void sjcd_get_status(void) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: get_status\n"); -#endif - sjcd_send_cmd(SCMD_GET_STATUS); - sjcd_receive_status(); -} - -/* - * Check the drive if the disk is changed. Should be revised. - */ -static int sjcd_disk_change(struct gendisk *disk) -{ -#if 0 - printk("SJCD: sjcd_disk_change(%s)\n", disk->disk_name); -#endif - if (!sjcd_command_is_in_progress) - sjcd_get_status(); - return (sjcd_status_valid ? sjcd_media_is_changed : 0); -} - -/* - * Read the table of contents (TOC) and TOC header if necessary. - * We assume that the drive contains no more than 99 toc entries. - */ -static struct sjcd_hw_disk_info sjcd_table_of_contents[SJCD_MAX_TRACKS]; -static unsigned char sjcd_first_track_no, sjcd_last_track_no; -#define sjcd_disk_length sjcd_table_of_contents[0].un.track_msf - -static int sjcd_update_toc(void) -{ - struct sjcd_hw_disk_info info; - int i; -#if defined( SJCD_TRACE ) - printk("SJCD: update toc:\n"); -#endif - /* - * check to see if we need to do anything - */ - if (sjcd_toc_uptodate) - return (0); - - /* - * Get the TOC start information. - */ - sjcd_send_1_cmd(SCMD_GET_DISK_INFO, SCMD_GET_1_TRACK); - sjcd_receive_status(); - - if (!sjcd_status_valid) { - printk("SJCD: cannot load status.\n"); - return (-1); - } - - if (!sjcd_media_is_available) { - printk("SJCD: no disk in drive\n"); - return (-1); - } - - if (!sjcd_command_failed) { - if (sjcd_load_response(&info, sizeof(info)) != 0) { - printk - ("SJCD: cannot load response about TOC start.\n"); - return (-1); - } - sjcd_first_track_no = bcd2bin(info.un.track_no); - } else { - printk("SJCD: get first failed\n"); - return (-1); - } -#if defined( SJCD_TRACE ) - printk("SJCD: TOC start 0x%02x ", sjcd_first_track_no); -#endif - /* - * Get the TOC finish information. - */ - sjcd_send_1_cmd(SCMD_GET_DISK_INFO, SCMD_GET_L_TRACK); - sjcd_receive_status(); - - if (!sjcd_status_valid) { - printk("SJCD: cannot load status.\n"); - return (-1); - } - - if (!sjcd_media_is_available) { - printk("SJCD: no disk in drive\n"); - return (-1); - } - - if (!sjcd_command_failed) { - if (sjcd_load_response(&info, sizeof(info)) != 0) { - printk - ("SJCD: cannot load response about TOC finish.\n"); - return (-1); - } - sjcd_last_track_no = bcd2bin(info.un.track_no); - } else { - printk("SJCD: get last failed\n"); - return (-1); - } -#if defined( SJCD_TRACE ) - printk("SJCD: TOC finish 0x%02x ", sjcd_last_track_no); -#endif - for (i = sjcd_first_track_no; i <= sjcd_last_track_no; i++) { - /* - * Get the first track information. - */ - sjcd_send_1_cmd(SCMD_GET_DISK_INFO, bin2bcd(i)); - sjcd_receive_status(); - - if (!sjcd_status_valid) { - printk("SJCD: cannot load status.\n"); - return (-1); - } - - if (!sjcd_media_is_available) { - printk("SJCD: no disk in drive\n"); - return (-1); - } - - if (!sjcd_command_failed) { - if (sjcd_load_response(&sjcd_table_of_contents[i], - sizeof(struct - sjcd_hw_disk_info)) - != 0) { - printk - ("SJCD: cannot load info for %d track\n", - i); - return (-1); - } - } else { - printk("SJCD: get info %d failed\n", i); - return (-1); - } - } - - /* - * Get the disk length info. - */ - sjcd_send_1_cmd(SCMD_GET_DISK_INFO, SCMD_GET_D_SIZE); - sjcd_receive_status(); - - if (!sjcd_status_valid) { - printk("SJCD: cannot load status.\n"); - return (-1); - } - - if (!sjcd_media_is_available) { - printk("SJCD: no disk in drive\n"); - return (-1); - } - - if (!sjcd_command_failed) { - if (sjcd_load_response(&info, sizeof(info)) != 0) { - printk - ("SJCD: cannot load response about disk size.\n"); - return (-1); - } - sjcd_disk_length.min = info.un.track_msf.min; - sjcd_disk_length.sec = info.un.track_msf.sec; - sjcd_disk_length.frame = info.un.track_msf.frame; - } else { - printk("SJCD: get size failed\n"); - return (1); - } -#if defined( SJCD_TRACE ) - printk("SJCD: (%02x:%02x.%02x)\n", sjcd_disk_length.min, - sjcd_disk_length.sec, sjcd_disk_length.frame); -#endif - return (0); -} - -/* - * Load subchannel information. - */ -static int sjcd_get_q_info(struct sjcd_hw_qinfo *qp) -{ - int s; -#if defined( SJCD_TRACE ) - printk("SJCD: load sub q\n"); -#endif - sjcd_send_cmd(SCMD_GET_QINFO); - s = sjcd_receive_status(); - if (s < 0 || sjcd_command_failed || !sjcd_status_valid) { - sjcd_send_cmd(0xF2); - s = sjcd_receive_status(); - if (s < 0 || sjcd_command_failed || !sjcd_status_valid) - return (-1); - sjcd_send_cmd(SCMD_GET_QINFO); - s = sjcd_receive_status(); - if (s < 0 || sjcd_command_failed || !sjcd_status_valid) - return (-1); - } - if (sjcd_media_is_available) - if (sjcd_load_response(qp, sizeof(*qp)) == 0) - return (0); - return (-1); -} - -/* - * Start playing from the specified position. - */ -static int sjcd_play(struct sjcd_play_msf *mp) -{ - struct sjcd_play_msf msf; - - /* - * Turn the device to play mode. - */ - sjcd_send_1_cmd(SCMD_SET_MODE, SCMD_MODE_PLAY); - if (sjcd_receive_status() < 0) - return (-1); - - /* - * Seek to the starting point. - */ - msf.start = mp->start; - msf.end.min = msf.end.sec = msf.end.frame = 0x00; - sjcd_send_6_cmd(SCMD_SEEK, &msf); - if (sjcd_receive_status() < 0) - return (-1); - - /* - * Start playing. - */ - sjcd_send_6_cmd(SCMD_PLAY, mp); - return (sjcd_receive_status()); -} - -/* - * Tray control functions. - */ -static int sjcd_tray_close(void) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: tray_close\n"); -#endif - sjcd_send_cmd(SCMD_CLOSE_TRAY); - return (sjcd_receive_status()); -} - -static int sjcd_tray_lock(void) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: tray_lock\n"); -#endif - sjcd_send_cmd(SCMD_LOCK_TRAY); - return (sjcd_receive_status()); -} - -static int sjcd_tray_unlock(void) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: tray_unlock\n"); -#endif - sjcd_send_cmd(SCMD_UNLOCK_TRAY); - return (sjcd_receive_status()); -} - -static int sjcd_tray_open(void) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: tray_open\n"); -#endif - sjcd_send_cmd(SCMD_EJECT_TRAY); - return (sjcd_receive_status()); -} - -/* - * Do some user commands. - */ -static int sjcd_ioctl(struct inode *ip, struct file *fp, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; -#if defined( SJCD_TRACE ) - printk("SJCD:ioctl\n"); -#endif - - sjcd_get_status(); - if (!sjcd_status_valid) - return (-EIO); - if (sjcd_update_toc() < 0) - return (-EIO); - - switch (cmd) { - case CDROMSTART:{ -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: start\n"); -#endif - return (0); - } - - case CDROMSTOP:{ -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: stop\n"); -#endif - sjcd_send_cmd(SCMD_PAUSE); - (void) sjcd_receive_status(); - sjcd_audio_status = CDROM_AUDIO_NO_STATUS; - return (0); - } - - case CDROMPAUSE:{ - struct sjcd_hw_qinfo q_info; -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: pause\n"); -#endif - if (sjcd_audio_status == CDROM_AUDIO_PLAY) { - sjcd_send_cmd(SCMD_PAUSE); - (void) sjcd_receive_status(); - if (sjcd_get_q_info(&q_info) < 0) { - sjcd_audio_status = - CDROM_AUDIO_NO_STATUS; - } else { - sjcd_audio_status = - CDROM_AUDIO_PAUSED; - sjcd_playing.start = q_info.abs; - } - return (0); - } else - return (-EINVAL); - } - - case CDROMRESUME:{ -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: resume\n"); -#endif - if (sjcd_audio_status == CDROM_AUDIO_PAUSED) { - /* - * continue play starting at saved location - */ - if (sjcd_play(&sjcd_playing) < 0) { - sjcd_audio_status = - CDROM_AUDIO_ERROR; - return (-EIO); - } else { - sjcd_audio_status = - CDROM_AUDIO_PLAY; - return (0); - } - } else - return (-EINVAL); - } - - case CDROMPLAYTRKIND:{ - struct cdrom_ti ti; - int s = -EFAULT; -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: playtrkind\n"); -#endif - if (!copy_from_user(&ti, argp, sizeof(ti))) { - s = 0; - if (ti.cdti_trk0 < sjcd_first_track_no) - return (-EINVAL); - if (ti.cdti_trk1 > sjcd_last_track_no) - ti.cdti_trk1 = sjcd_last_track_no; - if (ti.cdti_trk0 > ti.cdti_trk1) - return (-EINVAL); - - sjcd_playing.start = - sjcd_table_of_contents[ti.cdti_trk0]. - un.track_msf; - sjcd_playing.end = - (ti.cdti_trk1 < - sjcd_last_track_no) ? - sjcd_table_of_contents[ti.cdti_trk1 + - 1].un. - track_msf : sjcd_table_of_contents[0]. - un.track_msf; - - if (sjcd_play(&sjcd_playing) < 0) { - sjcd_audio_status = - CDROM_AUDIO_ERROR; - return (-EIO); - } else - sjcd_audio_status = - CDROM_AUDIO_PLAY; - } - return (s); - } - - case CDROMPLAYMSF:{ - struct cdrom_msf sjcd_msf; - int s; -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: playmsf\n"); -#endif - if ((s = - access_ok(VERIFY_READ, argp, sizeof(sjcd_msf)) - ? 0 : -EFAULT) == 0) { - if (sjcd_audio_status == CDROM_AUDIO_PLAY) { - sjcd_send_cmd(SCMD_PAUSE); - (void) sjcd_receive_status(); - sjcd_audio_status = - CDROM_AUDIO_NO_STATUS; - } - - if (copy_from_user(&sjcd_msf, argp, - sizeof(sjcd_msf))) - return (-EFAULT); - - sjcd_playing.start.min = - bin2bcd(sjcd_msf.cdmsf_min0); - sjcd_playing.start.sec = - bin2bcd(sjcd_msf.cdmsf_sec0); - sjcd_playing.start.frame = - bin2bcd(sjcd_msf.cdmsf_frame0); - sjcd_playing.end.min = - bin2bcd(sjcd_msf.cdmsf_min1); - sjcd_playing.end.sec = - bin2bcd(sjcd_msf.cdmsf_sec1); - sjcd_playing.end.frame = - bin2bcd(sjcd_msf.cdmsf_frame1); - - if (sjcd_play(&sjcd_playing) < 0) { - sjcd_audio_status = - CDROM_AUDIO_ERROR; - return (-EIO); - } else - sjcd_audio_status = - CDROM_AUDIO_PLAY; - } - return (s); - } - - case CDROMREADTOCHDR:{ - struct cdrom_tochdr toc_header; -#if defined (SJCD_TRACE ) - printk("SJCD: ioctl: readtocheader\n"); -#endif - toc_header.cdth_trk0 = sjcd_first_track_no; - toc_header.cdth_trk1 = sjcd_last_track_no; - if (copy_to_user(argp, &toc_header, - sizeof(toc_header))) - return -EFAULT; - return 0; - } - - case CDROMREADTOCENTRY:{ - struct cdrom_tocentry toc_entry; - int s; -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: readtocentry\n"); -#endif - if ((s = - access_ok(VERIFY_WRITE, argp, sizeof(toc_entry)) - ? 0 : -EFAULT) == 0) { - struct sjcd_hw_disk_info *tp; - - if (copy_from_user(&toc_entry, argp, - sizeof(toc_entry))) - return (-EFAULT); - if (toc_entry.cdte_track == CDROM_LEADOUT) - tp = &sjcd_table_of_contents[0]; - else if (toc_entry.cdte_track < - sjcd_first_track_no) - return (-EINVAL); - else if (toc_entry.cdte_track > - sjcd_last_track_no) - return (-EINVAL); - else - tp = &sjcd_table_of_contents - [toc_entry.cdte_track]; - - toc_entry.cdte_adr = - tp->track_control & 0x0F; - toc_entry.cdte_ctrl = - tp->track_control >> 4; - - switch (toc_entry.cdte_format) { - case CDROM_LBA: - toc_entry.cdte_addr.lba = - msf2hsg(&(tp->un.track_msf)); - break; - case CDROM_MSF: - toc_entry.cdte_addr.msf.minute = - bcd2bin(tp->un.track_msf.min); - toc_entry.cdte_addr.msf.second = - bcd2bin(tp->un.track_msf.sec); - toc_entry.cdte_addr.msf.frame = - bcd2bin(tp->un.track_msf. - frame); - break; - default: - return (-EINVAL); - } - if (copy_to_user(argp, &toc_entry, - sizeof(toc_entry))) - s = -EFAULT; - } - return (s); - } - - case CDROMSUBCHNL:{ - struct cdrom_subchnl subchnl; - int s; -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: subchnl\n"); -#endif - if ((s = - access_ok(VERIFY_WRITE, argp, sizeof(subchnl)) - ? 0 : -EFAULT) == 0) { - struct sjcd_hw_qinfo q_info; - - if (copy_from_user(&subchnl, argp, - sizeof(subchnl))) - return (-EFAULT); - - if (sjcd_get_q_info(&q_info) < 0) - return (-EIO); - - subchnl.cdsc_audiostatus = - sjcd_audio_status; - subchnl.cdsc_adr = - q_info.track_control & 0x0F; - subchnl.cdsc_ctrl = - q_info.track_control >> 4; - subchnl.cdsc_trk = - bcd2bin(q_info.track_no); - subchnl.cdsc_ind = bcd2bin(q_info.x); - - switch (subchnl.cdsc_format) { - case CDROM_LBA: - subchnl.cdsc_absaddr.lba = - msf2hsg(&(q_info.abs)); - subchnl.cdsc_reladdr.lba = - msf2hsg(&(q_info.rel)); - break; - case CDROM_MSF: - subchnl.cdsc_absaddr.msf.minute = - bcd2bin(q_info.abs.min); - subchnl.cdsc_absaddr.msf.second = - bcd2bin(q_info.abs.sec); - subchnl.cdsc_absaddr.msf.frame = - bcd2bin(q_info.abs.frame); - subchnl.cdsc_reladdr.msf.minute = - bcd2bin(q_info.rel.min); - subchnl.cdsc_reladdr.msf.second = - bcd2bin(q_info.rel.sec); - subchnl.cdsc_reladdr.msf.frame = - bcd2bin(q_info.rel.frame); - break; - default: - return (-EINVAL); - } - if (copy_to_user(argp, &subchnl, - sizeof(subchnl))) - s = -EFAULT; - } - return (s); - } - - case CDROMVOLCTRL:{ - struct cdrom_volctrl vol_ctrl; - int s; -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: volctrl\n"); -#endif - if ((s = - access_ok(VERIFY_READ, argp, sizeof(vol_ctrl)) - ? 0 : -EFAULT) == 0) { - unsigned char dummy[4]; - - if (copy_from_user(&vol_ctrl, argp, - sizeof(vol_ctrl))) - return (-EFAULT); - sjcd_send_4_cmd(SCMD_SET_VOLUME, - vol_ctrl.channel0, 0xFF, - vol_ctrl.channel1, 0xFF); - if (sjcd_receive_status() < 0) - return (-EIO); - (void) sjcd_load_response(dummy, 4); - } - return (s); - } - - case CDROMEJECT:{ -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: eject\n"); -#endif - if (!sjcd_command_is_in_progress) { - sjcd_tray_unlock(); - sjcd_send_cmd(SCMD_EJECT_TRAY); - (void) sjcd_receive_status(); - } - return (0); - } - -#if defined( SJCD_GATHER_STAT ) - case 0xABCD:{ -#if defined( SJCD_TRACE ) - printk("SJCD: ioctl: statistic\n"); -#endif - if (copy_to_user(argp, &statistic, sizeof(statistic))) - return -EFAULT; - return 0; - } -#endif - - default: - return (-EINVAL); - } -} - -/* - * Invalidate internal buffers of the driver. - */ -static void sjcd_invalidate_buffers(void) -{ - int i; - for (i = 0; i < SJCD_BUF_SIZ; sjcd_buf_bn[i++] = -1); - sjcd_buf_out = -1; -} - -/* - * Take care of the different block sizes between cdrom and Linux. - * When Linux gets variable block sizes this will probably go away. - */ - -static int current_valid(void) -{ - return CURRENT && - CURRENT->cmd == READ && - CURRENT->sector != -1; -} - -static void sjcd_transfer(void) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: transfer:\n"); -#endif - if (current_valid()) { - while (CURRENT->nr_sectors) { - int i, bn = CURRENT->sector / 4; - for (i = 0; - i < SJCD_BUF_SIZ && sjcd_buf_bn[i] != bn; - i++); - if (i < SJCD_BUF_SIZ) { - int offs = - (i * 4 + (CURRENT->sector & 3)) * 512; - int nr_sectors = 4 - (CURRENT->sector & 3); - if (sjcd_buf_out != i) { - sjcd_buf_out = i; - if (sjcd_buf_bn[i] != bn) { - sjcd_buf_out = -1; - continue; - } - } - if (nr_sectors > CURRENT->nr_sectors) - nr_sectors = CURRENT->nr_sectors; -#if defined( SJCD_TRACE ) - printk("SJCD: copy out\n"); -#endif - memcpy(CURRENT->buffer, sjcd_buf + offs, - nr_sectors * 512); - CURRENT->nr_sectors -= nr_sectors; - CURRENT->sector += nr_sectors; - CURRENT->buffer += nr_sectors * 512; - } else { - sjcd_buf_out = -1; - break; - } - } - } -#if defined( SJCD_TRACE ) - printk("SJCD: transfer: done\n"); -#endif -} - -static void sjcd_poll(void) -{ -#if defined( SJCD_GATHER_STAT ) - /* - * Update total number of ticks. - */ - statistic.ticks++; - statistic.tticks[sjcd_transfer_state]++; -#endif - - ReSwitch:switch (sjcd_transfer_state) { - - case SJCD_S_IDLE:{ -#if defined( SJCD_GATHER_STAT ) - statistic.idle_ticks++; -#endif -#if defined( SJCD_TRACE ) - printk("SJCD_S_IDLE\n"); -#endif - return; - } - - case SJCD_S_START:{ -#if defined( SJCD_GATHER_STAT ) - statistic.start_ticks++; -#endif - sjcd_send_cmd(SCMD_GET_STATUS); - sjcd_transfer_state = - sjcd_mode == - SCMD_MODE_COOKED ? SJCD_S_READ : SJCD_S_MODE; - sjcd_transfer_timeout = 500; -#if defined( SJCD_TRACE ) - printk("SJCD_S_START: goto SJCD_S_%s mode\n", - sjcd_transfer_state == - SJCD_S_READ ? "READ" : "MODE"); -#endif - break; - } - - case SJCD_S_MODE:{ - if (sjcd_check_status()) { - /* - * Previous command is completed. - */ - if (!sjcd_status_valid - || sjcd_command_failed) { -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_MODE: pre-cmd failed: goto to SJCD_S_STOP mode\n"); -#endif - sjcd_transfer_state = SJCD_S_STOP; - goto ReSwitch; - } - - sjcd_mode = 0; /* unknown mode; should not be valid when failed */ - sjcd_send_1_cmd(SCMD_SET_MODE, - SCMD_MODE_COOKED); - sjcd_transfer_state = SJCD_S_READ; - sjcd_transfer_timeout = 1000; -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_MODE: goto SJCD_S_READ mode\n"); -#endif - } -#if defined( SJCD_GATHER_STAT ) - else - statistic.mode_ticks++; -#endif - break; - } - - case SJCD_S_READ:{ - if (sjcd_status_valid ? 1 : sjcd_check_status()) { - /* - * Previous command is completed. - */ - if (!sjcd_status_valid - || sjcd_command_failed) { -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_READ: pre-cmd failed: goto to SJCD_S_STOP mode\n"); -#endif - sjcd_transfer_state = SJCD_S_STOP; - goto ReSwitch; - } - if (!sjcd_media_is_available) { -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_READ: no disk: goto to SJCD_S_STOP mode\n"); -#endif - sjcd_transfer_state = SJCD_S_STOP; - goto ReSwitch; - } - if (sjcd_mode != SCMD_MODE_COOKED) { - /* - * We seem to come from set mode. So discard one byte of result. - */ - if (sjcd_load_response - (&sjcd_mode, 1) != 0) { -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_READ: load failed: goto to SJCD_S_STOP mode\n"); -#endif - sjcd_transfer_state = - SJCD_S_STOP; - goto ReSwitch; - } - if (sjcd_mode != SCMD_MODE_COOKED) { -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_READ: mode failed: goto to SJCD_S_STOP mode\n"); -#endif - sjcd_transfer_state = - SJCD_S_STOP; - goto ReSwitch; - } - } - - if (current_valid()) { - struct sjcd_play_msf msf; - - sjcd_next_bn = CURRENT->sector / 4; - hsg2msf(sjcd_next_bn, &msf.start); - msf.end.min = 0; - msf.end.sec = 0; - msf.end.frame = sjcd_read_count = - SJCD_BUF_SIZ; -#if defined( SJCD_TRACE ) - printk - ("SJCD: ---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 - ("sjcd_next_bn:%x buf_in:%x buf_out:%x buf_bn:%x\n", - sjcd_next_bn, sjcd_buf_in, - sjcd_buf_out, - sjcd_buf_bn[sjcd_buf_in]); -#endif - sjcd_send_6_cmd(SCMD_DATA_READ, - &msf); - sjcd_transfer_state = SJCD_S_DATA; - sjcd_transfer_timeout = 500; -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_READ: go to SJCD_S_DATA mode\n"); -#endif - } else { -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_READ: nothing to read: go to SJCD_S_STOP mode\n"); -#endif - sjcd_transfer_state = SJCD_S_STOP; - goto ReSwitch; - } - } -#if defined( SJCD_GATHER_STAT ) - else - statistic.read_ticks++; -#endif - break; - } - - case SJCD_S_DATA:{ - unsigned char stat; - - sjcd_s_data:stat = - inb(SJCDPORT - (1)); -#if defined( SJCD_TRACE ) - printk("SJCD_S_DATA: status = 0x%02x\n", stat); -#endif - if (SJCD_STATUS_AVAILABLE(stat)) { - /* - * No data is waiting for us in the drive buffer. Status of operation - * completion is available. Read and parse it. - */ - sjcd_load_status(); - - if (!sjcd_status_valid - || sjcd_command_failed) { -#if defined( SJCD_TRACE ) - printk - ("SJCD: read block %d failed, maybe audio disk? Giving up\n", - sjcd_next_bn); -#endif - if (current_valid()) - end_request(CURRENT, 0); -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_DATA: pre-cmd failed: go to SJCD_S_STOP mode\n"); -#endif - sjcd_transfer_state = SJCD_S_STOP; - goto ReSwitch; - } - - if (!sjcd_media_is_available) { - printk - ("SJCD_S_DATA: no disk: go to SJCD_S_STOP mode\n"); - sjcd_transfer_state = SJCD_S_STOP; - goto ReSwitch; - } - - sjcd_transfer_state = SJCD_S_READ; - goto ReSwitch; - } else if (SJCD_DATA_AVAILABLE(stat)) { - /* - * One frame is read into device buffer. We must copy it to our memory. - * Otherwise cdrom hangs up. Check to see if we have something to copy - * to. - */ - if (!current_valid() - && sjcd_buf_in == sjcd_buf_out) { -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_DATA: nothing to read: go to SJCD_S_STOP mode\n"); - printk - (" ... all the date would be discarded\n"); -#endif - sjcd_transfer_state = SJCD_S_STOP; - goto ReSwitch; - } - - /* - * Everything seems to be OK. Just read the frame and recalculate - * indices. - */ - sjcd_buf_bn[sjcd_buf_in] = -1; /* ??? */ - insb(SJCDPORT(2), - sjcd_buf + 2048 * sjcd_buf_in, 2048); -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_DATA: next_bn=%d, buf_in=%d, buf_out=%d, buf_bn=%d\n", - sjcd_next_bn, sjcd_buf_in, - sjcd_buf_out, - sjcd_buf_bn[sjcd_buf_in]); -#endif - sjcd_buf_bn[sjcd_buf_in] = sjcd_next_bn++; - if (sjcd_buf_out == -1) - sjcd_buf_out = sjcd_buf_in; - if (++sjcd_buf_in == SJCD_BUF_SIZ) - sjcd_buf_in = 0; - - /* - * Only one frame is ready at time. So we should turn over to wait for - * another frame. If we need that, of course. - */ - if (--sjcd_read_count == 0) { - /* - * OK, request seems to be precessed. Continue transferring... - */ - if (!sjcd_transfer_is_active) { - while (current_valid()) { - /* - * Continue transferring. - */ - sjcd_transfer(); - if (CURRENT-> - nr_sectors == - 0) - end_request - (CURRENT, 1); - else - break; - } - } - if (current_valid() && - (CURRENT->sector / 4 < - sjcd_next_bn - || CURRENT->sector / 4 > - sjcd_next_bn + - SJCD_BUF_SIZ)) { -#if defined( SJCD_TRACE ) - printk - ("SJCD_S_DATA: can't read: go to SJCD_S_STOP mode\n"); -#endif - sjcd_transfer_state = - SJCD_S_STOP; - goto ReSwitch; - } - } - /* - * Now we should turn around rather than wait for while. - */ - goto sjcd_s_data; - } -#if defined( SJCD_GATHER_STAT ) - else - statistic.data_ticks++; -#endif - break; - } - - case SJCD_S_STOP:{ - sjcd_read_count = 0; - sjcd_send_cmd(SCMD_STOP); - sjcd_transfer_state = SJCD_S_STOPPING; - sjcd_transfer_timeout = 500; -#if defined( SJCD_GATHER_STAT ) - statistic.stop_ticks++; -#endif - break; - } - - case SJCD_S_STOPPING:{ - unsigned char stat; - - stat = inb(SJCDPORT(1)); -#if defined( SJCD_TRACE ) - printk("SJCD_S_STOP: status = 0x%02x\n", stat); -#endif - if (SJCD_DATA_AVAILABLE(stat)) { - int i; -#if defined( SJCD_TRACE ) - printk("SJCD_S_STOP: discard data\n"); -#endif - /* - * Discard all the data from the pipe. Foolish method. - */ - for (i = 2048; i--; - (void) inb(SJCDPORT(2))); - sjcd_transfer_timeout = 500; - } else if (SJCD_STATUS_AVAILABLE(stat)) { - sjcd_load_status(); - if (sjcd_status_valid - && sjcd_media_is_changed) { - sjcd_toc_uptodate = 0; - sjcd_invalidate_buffers(); - } - if (current_valid()) { - if (sjcd_status_valid) - sjcd_transfer_state = - SJCD_S_READ; - else - sjcd_transfer_state = - SJCD_S_START; - } else - sjcd_transfer_state = SJCD_S_IDLE; - goto ReSwitch; - } -#if defined( SJCD_GATHER_STAT ) - else - statistic.stopping_ticks++; -#endif - break; - } - - default: - printk("SJCD: poll: invalid state %d\n", - sjcd_transfer_state); - return; - } - - if (--sjcd_transfer_timeout == 0) { - printk("SJCD: timeout in state %d\n", sjcd_transfer_state); - while (current_valid()) - end_request(CURRENT, 0); - sjcd_send_cmd(SCMD_STOP); - sjcd_transfer_state = SJCD_S_IDLE; - goto ReSwitch; - } - - /* - * Get back in some time. 1 should be replaced with count variable to - * avoid unnecessary testings. - */ - SJCD_SET_TIMER(sjcd_poll, 1); -} - -static void do_sjcd_request(request_queue_t * q) -{ -#if defined( SJCD_TRACE ) - printk("SJCD: do_sjcd_request(%ld+%ld)\n", - CURRENT->sector, CURRENT->nr_sectors); -#endif - sjcd_transfer_is_active = 1; - while (current_valid()) { - sjcd_transfer(); - if (CURRENT->nr_sectors == 0) - end_request(CURRENT, 1); - else { - sjcd_buf_out = -1; /* Want to read a block not in buffer */ - if (sjcd_transfer_state == SJCD_S_IDLE) { - if (!sjcd_toc_uptodate) { - if (sjcd_update_toc() < 0) { - printk - ("SJCD: transfer: discard\n"); - while (current_valid()) - end_request(CURRENT, 0); - break; - } - } - sjcd_transfer_state = SJCD_S_START; - SJCD_SET_TIMER(sjcd_poll, HZ / 100); - } - break; - } - } - sjcd_transfer_is_active = 0; -#if defined( SJCD_TRACE ) - printk - ("sjcd_next_bn:%x sjcd_buf_in:%x sjcd_buf_out:%x sjcd_buf_bn:%x\n", - sjcd_next_bn, sjcd_buf_in, sjcd_buf_out, - sjcd_buf_bn[sjcd_buf_in]); - printk("do_sjcd_request ends\n"); -#endif -} - -/* - * Open the device special file. Check disk is in. - */ -static int sjcd_open(struct inode *ip, struct file *fp) -{ - /* - * Check the presence of device. - */ - if (!sjcd_present) - return (-ENXIO); - - /* - * Only read operations are allowed. Really? (:-) - */ - if (fp->f_mode & 2) - return (-EROFS); - - if (sjcd_open_count == 0) { - int s, sjcd_open_tries; -/* We don't know that, do we? */ -/* - sjcd_audio_status = CDROM_AUDIO_NO_STATUS; -*/ - sjcd_mode = 0; - sjcd_door_was_open = 0; - sjcd_transfer_state = SJCD_S_IDLE; - sjcd_invalidate_buffers(); - sjcd_status_valid = 0; - - /* - * Strict status checking. - */ - for (sjcd_open_tries = 4; --sjcd_open_tries;) { - if (!sjcd_status_valid) - sjcd_get_status(); - if (!sjcd_status_valid) { -#if defined( SJCD_DIAGNOSTIC ) - printk - ("SJCD: open: timed out when check status.\n"); -#endif - goto err_out; - } else if (!sjcd_media_is_available) { -#if defined( SJCD_DIAGNOSTIC ) - printk("SJCD: open: no disk in drive\n"); -#endif - if (!sjcd_door_closed) { - sjcd_door_was_open = 1; -#if defined( SJCD_TRACE ) - printk - ("SJCD: open: close the tray\n"); -#endif - s = sjcd_tray_close(); - if (s < 0 || !sjcd_status_valid - || sjcd_command_failed) { -#if defined( SJCD_DIAGNOSTIC ) - printk - ("SJCD: open: tray close attempt failed\n"); -#endif - goto err_out; - } - continue; - } else - goto err_out; - } - break; - } - s = sjcd_tray_lock(); - if (s < 0 || !sjcd_status_valid || sjcd_command_failed) { -#if defined( SJCD_DIAGNOSTIC ) - printk("SJCD: open: tray lock attempt failed\n"); -#endif - goto err_out; - } -#if defined( SJCD_TRACE ) - printk("SJCD: open: done\n"); -#endif - } - - ++sjcd_open_count; - return (0); - - err_out: - return (-EIO); -} - -/* - * On close, we flush all sjcd blocks from the buffer cache. - */ -static int sjcd_release(struct inode *inode, struct file *file) -{ - int s; - -#if defined( SJCD_TRACE ) - printk("SJCD: release\n"); -#endif - if (--sjcd_open_count == 0) { - sjcd_invalidate_buffers(); - s = sjcd_tray_unlock(); - if (s < 0 || !sjcd_status_valid || sjcd_command_failed) { -#if defined( SJCD_DIAGNOSTIC ) - printk - ("SJCD: release: tray unlock attempt failed.\n"); -#endif - } - if (sjcd_door_was_open) { - s = sjcd_tray_open(); - if (s < 0 || !sjcd_status_valid - || sjcd_command_failed) { -#if defined( SJCD_DIAGNOSTIC ) - printk - ("SJCD: release: tray unload attempt failed.\n"); -#endif - } - } - } - return 0; -} - -/* - * A list of file operations allowed for this cdrom. - */ -static struct block_device_operations sjcd_fops = { - .owner = THIS_MODULE, - .open = sjcd_open, - .release = sjcd_release, - .ioctl = sjcd_ioctl, - .media_changed = sjcd_disk_change, -}; - -/* - * Following stuff is intended for initialization of the cdrom. It - * first looks for presence of device. If the device is present, it - * will be reset. Then read the version of the drive and load status. - * The version is two BCD-coded bytes. - */ -static struct { - unsigned char major, minor; -} sjcd_version; - -static struct gendisk *sjcd_disk; - -/* - * Test for presence of drive and initialize it. Called at boot time. - * Probe cdrom, find out version and status. - */ -static int __init sjcd_init(void) -{ - int i; - - printk(KERN_INFO - "SJCD: Sanyo CDR-H94A cdrom driver version %d.%d.\n", - SJCD_VERSION_MAJOR, SJCD_VERSION_MINOR); - -#if defined( SJCD_TRACE ) - printk("SJCD: sjcd=0x%x: ", sjcd_base); -#endif - - if (register_blkdev(MAJOR_NR, "sjcd")) - return -EIO; - - sjcd_queue = blk_init_queue(do_sjcd_request, &sjcd_lock); - if (!sjcd_queue) - goto out0; - - blk_queue_hardsect_size(sjcd_queue, 2048); - - sjcd_disk = alloc_disk(1); - if (!sjcd_disk) { - printk(KERN_ERR "SJCD: can't allocate disk"); - goto out1; - } - sjcd_disk->major = MAJOR_NR, - sjcd_disk->first_minor = 0, - sjcd_disk->fops = &sjcd_fops, - sprintf(sjcd_disk->disk_name, "sjcd"); - - if (!request_region(sjcd_base, 4,"sjcd")) { - printk - ("SJCD: Init failed, I/O port (%X) is already in use\n", - sjcd_base); - goto out2; - } - - /* - * Check for card. Since we are booting now, we can't use standard - * wait algorithm. - */ - printk(KERN_INFO "SJCD: Resetting: "); - sjcd_send_cmd(SCMD_RESET); - for (i = 1000; i > 0 && !sjcd_status_valid; --i) { - unsigned long timer; - - /* - * Wait 10ms approx. - */ - for (timer = jiffies; time_before_eq(jiffies, timer);); - if ((i % 100) == 0) - printk("."); - (void) sjcd_check_status(); - } - if (i == 0 || sjcd_command_failed) { - printk(" reset failed, no drive found.\n"); - goto out3; - } else - printk("\n"); - - /* - * Get and print out cdrom version. - */ - printk(KERN_INFO "SJCD: Getting version: "); - sjcd_send_cmd(SCMD_GET_VERSION); - for (i = 1000; i > 0 && !sjcd_status_valid; --i) { - unsigned long timer; - - /* - * Wait 10ms approx. - */ - for (timer = jiffies; time_before_eq(jiffies, timer);); - if ((i % 100) == 0) - printk("."); - (void) sjcd_check_status(); - } - if (i == 0 || sjcd_command_failed) { - printk(" get version failed, no drive found.\n"); - goto out3; - } - - if (sjcd_load_response(&sjcd_version, sizeof(sjcd_version)) == 0) { - printk(" %1x.%02x\n", (int) sjcd_version.major, - (int) sjcd_version.minor); - } else { - printk(" read version failed, no drive found.\n"); - goto out3; - } - - /* - * Check and print out the tray state. (if it is needed?). - */ - if (!sjcd_status_valid) { - printk(KERN_INFO "SJCD: Getting status: "); - sjcd_send_cmd(SCMD_GET_STATUS); - for (i = 1000; i > 0 && !sjcd_status_valid; --i) { - unsigned long timer; - - /* - * Wait 10ms approx. - */ - for (timer = jiffies; - time_before_eq(jiffies, timer);); - if ((i % 100) == 0) - printk("."); - (void) sjcd_check_status(); - } - if (i == 0 || sjcd_command_failed) { - printk(" get status failed, no drive found.\n"); - goto out3; - } else - printk("\n"); - } - - printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base); - sjcd_disk->queue = sjcd_queue; - add_disk(sjcd_disk); - - sjcd_present++; - return (0); -out3: - release_region(sjcd_base, 4); -out2: - put_disk(sjcd_disk); -out1: - blk_cleanup_queue(sjcd_queue); -out0: - if ((unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL)) - printk("SJCD: cannot unregister device.\n"); - return (-EIO); -} - -static void __exit sjcd_exit(void) -{ - del_gendisk(sjcd_disk); - put_disk(sjcd_disk); - release_region(sjcd_base, 4); - blk_cleanup_queue(sjcd_queue); - if ((unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL)) - printk("SJCD: cannot unregister device.\n"); - printk(KERN_INFO "SJCD: module: removed.\n"); -} - -module_init(sjcd_init); -module_exit(sjcd_exit); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_BLOCKDEV_MAJOR(SANYO_CDROM_MAJOR); diff --git a/drivers/cdrom/sjcd.h b/drivers/cdrom/sjcd.h deleted file mode 100644 index 0aa5e714659..00000000000 --- a/drivers/cdrom/sjcd.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Definitions for a Sanyo CD-ROM interface. - * - * Copyright (C) 1995 Vadim V. Model - * model@cecmow.enet.dec.com - * vadim@rbrf.msk.su - * vadim@ipsun.ras.ru - * Eric van der Maarel - * H.T.M.v.d.Maarel@marin.nl - * - * This information is based on mcd.c from M. Harriss and sjcd102.lst from - * E. Moenkeberg. - * - * 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 __SJCD_H__ -#define __SJCD_H__ - -/* - * Change this to set the I/O port address as default. More flexibility - * come with setup implementation. - */ -#define SJCD_BASE_ADDR 0x340 - -/* - * Change this to set the irq as default. Really SANYO do not use interrupts - * at all. - */ -#define SJCD_INTR_NR 0 - -/* - * Change this to set the dma as default value. really SANYO does not use - * direct memory access at all. - */ -#define SJCD_DMA_NR 0 - -/* - * Macros which allow us to find out the status of the drive. - */ -#define SJCD_STATUS_AVAILABLE( x ) (((x)&0x02)==0) -#define SJCD_DATA_AVAILABLE( x ) (((x)&0x01)==0) - -/* - * Port access macro. Three ports are available: S-data port (command port), - * status port (read only) and D-data port (read only). - */ -#define SJCDPORT( x ) ( sjcd_base + ( x ) ) -#define SJCD_STATUS_PORT SJCDPORT( 1 ) -#define SJCD_S_DATA_PORT SJCDPORT( 0 ) -#define SJCD_COMMAND_PORT SJCDPORT( 0 ) -#define SJCD_D_DATA_PORT SJCDPORT( 2 ) - -/* - * Drive info bits. Drive info available as first (mandatory) byte of - * command completion status. - */ -#define SST_NOT_READY 0x10 /* no disk in the drive (???) */ -#define SST_MEDIA_CHANGED 0x20 /* disk is changed */ -#define SST_DOOR_OPENED 0x40 /* door is open */ - -/* commands */ - -#define SCMD_EJECT_TRAY 0xD0 /* eject tray if not locked */ -#define SCMD_LOCK_TRAY 0xD2 /* lock tray when in */ -#define SCMD_UNLOCK_TRAY 0xD4 /* unlock tray when in */ -#define SCMD_CLOSE_TRAY 0xD6 /* load tray in */ - -#define SCMD_RESET 0xFA /* soft reset */ -#define SCMD_GET_STATUS 0x80 -#define SCMD_GET_VERSION 0xCC - -#define SCMD_DATA_READ 0xA0 /* are the same, depend on mode&args */ -#define SCMD_SEEK 0xA0 -#define SCMD_PLAY 0xA0 - -#define SCMD_GET_QINFO 0xA8 - -#define SCMD_SET_MODE 0xC4 -#define SCMD_MODE_PLAY 0xE0 -#define SCMD_MODE_COOKED (0xF8 & ~0x20) -#define SCMD_MODE_RAW 0xF9 -#define SCMD_MODE_x20_BIT 0x20 /* What is it for ? */ - -#define SCMD_SET_VOLUME 0xAE -#define SCMD_PAUSE 0xE0 -#define SCMD_STOP 0xE0 - -#define SCMD_GET_DISK_INFO 0xAA - -/* - * Some standard arguments for SCMD_GET_DISK_INFO. - */ -#define SCMD_GET_1_TRACK 0xA0 /* get the first track information */ -#define SCMD_GET_L_TRACK 0xA1 /* get the last track information */ -#define SCMD_GET_D_SIZE 0xA2 /* get the whole disk information */ - -/* - * Borrowed from hd.c. Allows to optimize multiple port read commands. - */ -#define S_READ_DATA( port, buf, nr ) insb( port, buf, nr ) - -/* - * We assume that there are no audio disks with TOC length more than this - * number (I personally have never seen disks with more than 20 fragments). - */ -#define SJCD_MAX_TRACKS 100 - -struct msf { - unsigned char min; - unsigned char sec; - unsigned char frame; -}; - -struct sjcd_hw_disk_info { - unsigned char track_control; - unsigned char track_no; - unsigned char x, y, z; - union { - unsigned char track_no; - struct msf track_msf; - } un; -}; - -struct sjcd_hw_qinfo { - unsigned char track_control; - unsigned char track_no; - unsigned char x; - struct msf rel; - struct msf abs; -}; - -struct sjcd_play_msf { - struct msf start; - struct msf end; -}; - -struct sjcd_disk_info { - unsigned char first; - unsigned char last; - struct msf disk_length; - struct msf first_track; -}; - -struct sjcd_toc { - unsigned char ctrl_addr; - unsigned char track; - unsigned char point_index; - struct msf track_time; - struct msf disk_time; -}; - -#if defined( SJCD_GATHER_STAT ) - -struct sjcd_stat { - int ticks; - int tticks[ 8 ]; - int idle_ticks; - int start_ticks; - int mode_ticks; - int read_ticks; - int data_ticks; - int stop_ticks; - int stopping_ticks; -}; - -#endif - -#endif diff --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c deleted file mode 100644 index f77ada933ea..00000000000 --- a/drivers/cdrom/sonycd535.c +++ /dev/null @@ -1,1689 +0,0 @@ -/* - * Sony CDU-535 interface device driver - * - * This is a modified version of the CDU-31A device driver (see below). - * Changes were made using documentation for the CDU-531 (which Sony - * assures me is very similar to the 535) and partial disassembly of the - * DOS driver. I used Minyard's driver and replaced the CDU-31A - * commands with the CDU-531 commands. This was complicated by a different - * interface protocol with the drive. The driver is still polled. - * - * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec. - * I tried polling without the sony_sleep during the data transfers but - * it did not speed things up any. - * - * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict - * with CDU-31A driver. This is the also the number from the Linux - * Device Driver Registry for the Sony Drive. Hope nobody else is using it. - * - * 1993-08-29 (rgj) remove the configuring of the interface board address - * from the top level configuration, you have to modify it in this file. - * - * 1995-01-26 Made module-capable (Joel Katz <Stimpson@Panix.COM>) - * - * 1995-05-20 - * Modified to support CDU-510/515 series - * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>) - * Fixed to report verify_area() failures - * (Heiko Eissfeldt <heiko@colossus.escape.de>) - * - * 1995-06-01 - * More changes to support CDU-510/515 series - * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>) - * - * November 1999 -- Make kernel-parameter implementation work with 2.3.x - * Removed init_module & cleanup_module in favor of - * module_init & module_exit. - * Torben Mathiasen <tmm@image.dk> - * - * September 2003 - Fix SMP support by removing cli/sti calls. - * Using spinlocks with a wait_queue instead. - * Felipe Damasio <felipewd@terra.com.br> - * - * Things to do: - * - handle errors and status better, put everything into a single word - * - use interrupts (code mostly there, but a big hole still missing) - * - handle multi-session CDs? - * - use DMA? - * - * Known Bugs: - * - - * - * Ken Pizzini (ken@halcyon.com) - * - * Original by: - * Ron Jeppesen (ronj.an@site007.saic.com) - * - * - *------------------------------------------------------------------------ - * Sony CDROM interface device driver. - * - * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to Ken above) - * - * Colossians 3:17 - * - * The Sony interface device driver handles Sony interface CDROM - * drives and provides a complete block-level interface as well as an - * ioctl() interface compatible with the Sun (as specified in - * include/linux/cdrom.h). With this interface, CDROMs can be - * accessed and standard audio CDs can be played back normally. - * - * This interface is (unfortunately) a polled interface. This is - * because most Sony interfaces are set up with DMA and interrupts - * disables. Some (like mine) do not even have the capability to - * handle interrupts or DMA. For this reason you will see a bit of - * the following: - * - * snap = jiffies; - * while (jiffies-snap < SONY_JIFFIES_TIMEOUT) - * { - * if (some_condition()) - * break; - * sony_sleep(); - * } - * if (some_condition not met) - * { - * return an_error; - * } - * - * This ugly hack waits for something to happen, sleeping a little - * between every try. (The conditional is written so that jiffies - * wrap-around is handled properly.) - * - * One thing about these drives: They talk in MSF (Minute Second Frame) format. - * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a - * disk. The funny thing is that these are sent to the drive in BCD, but the - * interface wants to see them in decimal. A lot of conversion goes on. - * - * Copyright (C) 1993 Corey Minyard - * - * 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. - * - */ - - -# include <linux/module.h> - -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/hdreg.h> -#include <linux/genhd.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/init.h> - -#define REALLY_SLOW_IO -#include <asm/system.h> -#include <asm/io.h> -#include <asm/uaccess.h> - -#include <linux/cdrom.h> - -#define MAJOR_NR CDU535_CDROM_MAJOR -#include <linux/blkdev.h> - -#define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */ -#include "sonycd535.h" - -/* - * this is the base address of the interface card for the Sony CDU-535 - * CDROM drive. If your jumpers are set for an address other than - * this one (the default), change the following line to the - * proper address. - */ -#ifndef CDU535_ADDRESS -# define CDU535_ADDRESS 0x340 -#endif -#ifndef CDU535_INTERRUPT -# define CDU535_INTERRUPT 0 -#endif -#ifndef CDU535_HANDLE -# define CDU535_HANDLE "cdu535" -#endif -#ifndef CDU535_MESSAGE_NAME -# define CDU535_MESSAGE_NAME "Sony CDU-535" -#endif - -#define CDU535_BLOCK_SIZE 2048 - -#ifndef MAX_SPINUP_RETRY -# define MAX_SPINUP_RETRY 3 /* 1 is sufficient for most drives... */ -#endif -#ifndef RETRY_FOR_BAD_STATUS -# define RETRY_FOR_BAD_STATUS 100 /* in 10th of second */ -#endif - -#ifndef DEBUG -# define DEBUG 1 -#endif - -/* - * SONY535_BUFFER_SIZE determines the size of internal buffer used - * by the drive. It must be at least 2K and the larger the buffer - * the better the transfer rate. It does however take system memory. - * On my system I get the following transfer rates using dd to read - * 10 Mb off /dev/cdrom. - * - * 8K buffer 43 Kb/sec - * 16K buffer 66 Kb/sec - * 32K buffer 91 Kb/sec - * 64K buffer 111 Kb/sec - * 128K buffer 123 Kb/sec - * 512K buffer 123 Kb/sec - */ -#define SONY535_BUFFER_SIZE (64*1024) - -/* - * if LOCK_DOORS is defined then the eject button is disabled while - * the device is open. - */ -#ifndef NO_LOCK_DOORS -# define LOCK_DOORS -#endif - -static int read_subcode(void); -static void sony_get_toc(void); -static int cdu_open(struct inode *inode, struct file *filp); -static inline unsigned int int_to_bcd(unsigned int val); -static unsigned int bcd_to_int(unsigned int bcd); -static int do_sony_cmd(Byte * cmd, int nCmd, Byte status[2], - Byte * response, int n_response, int ignoreStatusBit7); - -/* The base I/O address of the Sony Interface. This is a variable (not a - #define) so it can be easily changed via some future ioctl() */ -static unsigned int sony535_cd_base_io = CDU535_ADDRESS; -module_param(sony535_cd_base_io, int, 0); - -/* - * The following are I/O addresses of the various registers for the drive. The - * comment for the base address also applies here. - */ -static unsigned short select_unit_reg; -static unsigned short result_reg; -static unsigned short command_reg; -static unsigned short read_status_reg; -static unsigned short data_reg; - -static DEFINE_SPINLOCK(sonycd535_lock); /* queue lock */ -static struct request_queue *sonycd535_queue; - -static int initialized; /* Has the drive been initialized? */ -static int sony_disc_changed = 1; /* Has the disk been changed - since the last check? */ -static int sony_toc_read; /* Has the table of contents been - read? */ -static unsigned int sony_buffer_size; /* Size in bytes of the read-ahead - buffer. */ -static unsigned int sony_buffer_sectors; /* Size (in 2048 byte records) of - the read-ahead buffer. */ -static unsigned int sony_usage; /* How many processes have the - drive open. */ - -static int sony_first_block = -1; /* First OS block (512 byte) in - the read-ahead buffer */ -static int sony_last_block = -1; /* Last OS block (512 byte) in - the read-ahead buffer */ - -static struct s535_sony_toc *sony_toc; /* Points to the table of - contents. */ - -static struct s535_sony_subcode *last_sony_subcode; /* Points to the last - subcode address read */ -static Byte **sony_buffer; /* Points to the pointers - to the sector buffers */ - -static int sony_inuse; /* is the drive in use? Only one - open at a time allowed */ - -/* - * The audio status uses the values from read subchannel data as specified - * in include/linux/cdrom.h. - */ -static int sony_audio_status = CDROM_AUDIO_NO_STATUS; - -/* - * The following are a hack for pausing and resuming audio play. The drive - * does not work as I would expect it, if you stop it then start it again, - * the drive seeks back to the beginning and starts over. This holds the - * position during a pause so a resume can restart it. It uses the - * audio status variable above to tell if it is paused. - * I just kept the CDU-31A driver behavior rather than using the PAUSE - * command on the CDU-535. - */ -static Byte cur_pos_msf[3]; -static Byte final_pos_msf[3]; - -/* What IRQ is the drive using? 0 if none. */ -static int sony535_irq_used = CDU535_INTERRUPT; - -/* The interrupt handler will wake this queue up when it gets an interrupt. */ -static DECLARE_WAIT_QUEUE_HEAD(cdu535_irq_wait); - - -/* - * This routine returns 1 if the disk has been changed since the last - * check or 0 if it hasn't. Setting flag to 0 resets the changed flag. - */ -static int -cdu535_check_media_change(struct gendisk *disk) -{ - /* if driver is not initialized, always return 0 */ - int retval = initialized ? sony_disc_changed : 0; - sony_disc_changed = 0; - return retval; -} - -static inline void -enable_interrupts(void) -{ -#ifdef USE_IRQ - /* - * This code was taken from cdu31a.c; it will not - * directly work for the cdu535 as written... - */ - curr_control_reg |= ( SONY_ATTN_INT_EN_BIT - | SONY_RES_RDY_INT_EN_BIT - | SONY_DATA_RDY_INT_EN_BIT); - outb(curr_control_reg, sony_cd_control_reg); -#endif -} - -static inline void -disable_interrupts(void) -{ -#ifdef USE_IRQ - /* - * This code was taken from cdu31a.c; it will not - * directly work for the cdu535 as written... - */ - curr_control_reg &= ~(SONY_ATTN_INT_EN_BIT - | SONY_RES_RDY_INT_EN_BIT - | SONY_DATA_RDY_INT_EN_BIT); - outb(curr_control_reg, sony_cd_control_reg); -#endif -} - -static irqreturn_t -cdu535_interrupt(int irq, void *dev_id) -{ - disable_interrupts(); - if (waitqueue_active(&cdu535_irq_wait)) { - wake_up(&cdu535_irq_wait); - return IRQ_HANDLED; - } - printk(CDU535_MESSAGE_NAME - ": Got an interrupt but nothing was waiting\n"); - return IRQ_NONE; -} - - -/* - * Wait a little while. - */ -static inline void -sony_sleep(void) -{ - if (sony535_irq_used <= 0) { /* poll */ - yield(); - } else { /* Interrupt driven */ - DEFINE_WAIT(wait); - - spin_lock_irq(&sonycd535_lock); - enable_interrupts(); - prepare_to_wait(&cdu535_irq_wait, &wait, TASK_INTERRUPTIBLE); - spin_unlock_irq(&sonycd535_lock); - schedule(); - finish_wait(&cdu535_irq_wait, &wait); - } -} - -/*------------------start of SONY CDU535 very specific ---------------------*/ - -/**************************************************************************** - * void select_unit( int unit_no ) - * - * Select the specified unit (0-3) so that subsequent commands reference it - ****************************************************************************/ -static void -select_unit(int unit_no) -{ - unsigned int select_mask = ~(1 << unit_no); - outb(select_mask, select_unit_reg); -} - -/*************************************************************************** - * int read_result_reg( Byte *data_ptr ) - * - * Read a result byte from the Sony CDU controller, store in location pointed - * to by data_ptr. Return zero on success, TIME_OUT if we did not receive - * data. - ***************************************************************************/ -static int -read_result_reg(Byte *data_ptr) -{ - unsigned long snap; - int read_status; - - snap = jiffies; - while (jiffies-snap < SONY_JIFFIES_TIMEOUT) { - read_status = inb(read_status_reg); - if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) { -#if DEBUG > 1 - printk(CDU535_MESSAGE_NAME - ": read_result_reg(): readStatReg = 0x%x\n", read_status); -#endif - *data_ptr = inb(result_reg); - return 0; - } else { - sony_sleep(); - } - } - printk(CDU535_MESSAGE_NAME " read_result_reg: TIME OUT!\n"); - return TIME_OUT; -} - -/**************************************************************************** - * int read_exec_status( Byte status[2] ) - * - * Read the execution status of the last command and put into status. - * Handles reading second status word if available. Returns 0 on success, - * TIME_OUT on failure. - ****************************************************************************/ -static int -read_exec_status(Byte status[2]) -{ - status[1] = 0; - if (read_result_reg(&(status[0])) != 0) - return TIME_OUT; - if ((status[0] & 0x80) != 0) { /* byte two follows */ - if (read_result_reg(&(status[1])) != 0) - return TIME_OUT; - } -#if DEBUG > 1 - printk(CDU535_MESSAGE_NAME ": read_exec_status: read 0x%x 0x%x\n", - status[0], status[1]); -#endif - return 0; -} - -/**************************************************************************** - * int check_drive_status( void ) - * - * Check the current drive status. Using this before executing a command - * takes care of the problem of unsolicited drive status-2 messages. - * Add a check of the audio status if we think the disk is playing. - ****************************************************************************/ -static int -check_drive_status(void) -{ - Byte status, e_status[2]; - int CDD, ATN; - Byte cmd; - - select_unit(0); - if (sony_audio_status == CDROM_AUDIO_PLAY) { /* check status */ - outb(SONY535_REQUEST_AUDIO_STATUS, command_reg); - if (read_result_reg(&status) == 0) { - switch (status) { - case 0x0: - break; /* play in progress */ - case 0x1: - break; /* paused */ - case 0x3: /* audio play completed */ - case 0x5: /* play not requested */ - sony_audio_status = CDROM_AUDIO_COMPLETED; - read_subcode(); - break; - case 0x4: /* error during play */ - sony_audio_status = CDROM_AUDIO_ERROR; - break; - } - } - } - /* now check drive status */ - outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg); - if (read_result_reg(&status) != 0) - return TIME_OUT; - -#if DEBUG > 1 - printk(CDU535_MESSAGE_NAME ": check_drive_status() got 0x%x\n", status); -#endif - - if (status == 0) - return 0; - - ATN = status & 0xf; - CDD = (status >> 4) & 0xf; - - switch (ATN) { - case 0x0: - break; /* go on to CDD stuff */ - case SONY535_ATN_BUSY: - if (initialized) - printk(CDU535_MESSAGE_NAME " error: drive busy\n"); - return CD_BUSY; - case SONY535_ATN_EJECT_IN_PROGRESS: - printk(CDU535_MESSAGE_NAME " error: eject in progress\n"); - sony_audio_status = CDROM_AUDIO_INVALID; - return CD_BUSY; - case SONY535_ATN_RESET_OCCURRED: - case SONY535_ATN_DISC_CHANGED: - case SONY535_ATN_RESET_AND_DISC_CHANGED: -#if DEBUG > 0 - printk(CDU535_MESSAGE_NAME " notice: reset occurred or disc changed\n"); -#endif - sony_disc_changed = 1; - sony_toc_read = 0; - sony_audio_status = CDROM_AUDIO_NO_STATUS; - sony_first_block = -1; - sony_last_block = -1; - if (initialized) { - cmd = SONY535_SPIN_UP; - do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0); - sony_get_toc(); - } - return 0; - default: - printk(CDU535_MESSAGE_NAME " error: drive busy (ATN=0x%x)\n", ATN); - return CD_BUSY; - } - switch (CDD) { /* the 531 docs are not helpful in decoding this */ - case 0x0: /* just use the values from the DOS driver */ - case 0x2: - case 0xa: - break; /* no error */ - case 0xc: - printk(CDU535_MESSAGE_NAME - ": check_drive_status(): CDD = 0xc! Not properly handled!\n"); - return CD_BUSY; /* ? */ - default: - return CD_BUSY; - } - return 0; -} /* check_drive_status() */ - -/***************************************************************************** - * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2], - * Byte *response, int n_response, int ignore_status_bit7 ) - * - * Generic routine for executing commands. The command and its parameters - * should be placed in the cmd[] array, number of bytes in the command is - * stored in nCmd. The response from the command will be stored in the - * response array. The number of bytes you expect back (excluding status) - * should be passed in n_response. Finally, some - * commands set bit 7 of the return status even when there is no second - * status byte, on these commands set ignoreStatusBit7 TRUE. - * If the command was sent and data received back, then we return 0, - * else we return TIME_OUT. You still have to check the status yourself. - * You should call check_drive_status() before calling this routine - * so that you do not lose notifications of disk changes, etc. - ****************************************************************************/ -static int -do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2], - Byte * response, int n_response, int ignore_status_bit7) -{ - int i; - - /* write out the command */ - for (i = 0; i < n_cmd; i++) - outb(cmd[i], command_reg); - - /* read back the status */ - if (read_result_reg(status) != 0) - return TIME_OUT; - if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) { - /* get second status byte */ - if (read_result_reg(status + 1) != 0) - return TIME_OUT; - } else { - status[1] = 0; - } -#if DEBUG > 2 - printk(CDU535_MESSAGE_NAME ": do_sony_cmd %x: %x %x\n", - *cmd, status[0], status[1]); -#endif - - /* do not know about when I should read set of data and when not to */ - if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0) - return 0; - - /* else, read in rest of data */ - for (i = 0; 0 < n_response; n_response--, i++) - if (read_result_reg(response + i) != 0) - return TIME_OUT; - return 0; -} /* do_sony_cmd() */ - -/************************************************************************** - * int set_drive_mode( int mode, Byte status[2] ) - * - * Set the drive mode to the specified value (mode=0 is audio, mode=e0 - * is mode-1 CDROM - **************************************************************************/ -static int -set_drive_mode(int mode, Byte status[2]) -{ - Byte cmd_buff[2]; - Byte ret_buff[1]; - - cmd_buff[0] = SONY535_SET_DRIVE_MODE; - cmd_buff[1] = mode; - return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1); -} - -/*************************************************************************** - * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2], - * Byte *data_buff, int buff_size ) - * - * Read n_blocks of data from the CDROM starting at position params[0:2], - * number of blocks in stored in params[3:5] -- both these are already - * int bcd format. - * Transfer the data into the buffer pointed at by data_buff. buff_size - * gives the number of bytes available in the buffer. - * The routine returns number of bytes read in if successful, otherwise - * it returns one of the standard error returns. - ***************************************************************************/ -static int -seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2], - Byte **buff, int buf_size) -{ - Byte cmd_buff[7]; - int i; - int read_status; - unsigned long snap; - Byte *data_buff; - int sector_count = 0; - - if (buf_size < CDU535_BLOCK_SIZE * n_blocks) - return NO_ROOM; - - set_drive_mode(SONY535_CDROM_DRIVE_MODE, status); - - /* send command to read the data */ - cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1; - for (i = 0; i < 6; i++) - cmd_buff[i + 1] = params[i]; - for (i = 0; i < 7; i++) - outb(cmd_buff[i], command_reg); - - /* read back the data one block at a time */ - while (0 < n_blocks--) { - /* wait for data to be ready */ - int data_valid = 0; - snap = jiffies; - while (jiffies-snap < SONY_JIFFIES_TIMEOUT) { - read_status = inb(read_status_reg); - if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) { - read_exec_status(status); - return BAD_STATUS; - } - if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) { - /* data is ready, read it */ - data_buff = buff[sector_count++]; - for (i = 0; i < CDU535_BLOCK_SIZE; i++) - *data_buff++ = inb(data_reg); /* unrolling this loop does not seem to help */ - data_valid = 1; - break; /* exit the timeout loop */ - } - sony_sleep(); /* data not ready, sleep a while */ - } - if (!data_valid) - return TIME_OUT; /* if we reach this stage */ - } - - /* read all the data, now read the status */ - if ((i = read_exec_status(status)) != 0) - return i; - return CDU535_BLOCK_SIZE * sector_count; -} /* seek_and_read_N_blocks() */ - -/**************************************************************************** - * int request_toc_data( Byte status[2], struct s535_sony_toc *toc ) - * - * Read in the table of contents data. Converts all the bcd data - * into integers in the toc structure. - ****************************************************************************/ -static int -request_toc_data(Byte status[2], struct s535_sony_toc *toc) -{ - int to_status; - int i, j, n_tracks, track_no; - int first_track_num, last_track_num; - Byte cmd_no = 0xb2; - Byte track_address_buffer[5]; - - /* read the fixed portion of the table of contents */ - if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0) - return to_status; - - /* convert the data into integers so we can use them */ - first_track_num = bcd_to_int(toc->first_track_num); - last_track_num = bcd_to_int(toc->last_track_num); - n_tracks = last_track_num - first_track_num + 1; - - /* read each of the track address descriptors */ - for (i = 0; i < n_tracks; i++) { - /* read the descriptor into a temporary buffer */ - for (j = 0; j < 5; j++) { - if (read_result_reg(track_address_buffer + j) != 0) - return TIME_OUT; - if (j == 1) /* need to convert from bcd */ - track_no = bcd_to_int(track_address_buffer[j]); - } - /* copy the descriptor to proper location - sonycd.c just fills */ - memcpy(toc->tracks + i, track_address_buffer, 5); - } - return 0; -} /* request_toc_data() */ - -/*************************************************************************** - * int spin_up_drive( Byte status[2] ) - * - * Spin up the drive (unless it is already spinning). - ***************************************************************************/ -static int -spin_up_drive(Byte status[2]) -{ - Byte cmd; - - /* first see if the drive is already spinning */ - cmd = SONY535_REQUEST_DRIVE_STATUS_1; - if (do_sony_cmd(&cmd, 1, status, NULL, 0, 0) != 0) - return TIME_OUT; - if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0) - return 0; /* it's already spinning */ - - /* otherwise, give the spin-up command */ - cmd = SONY535_SPIN_UP; - return do_sony_cmd(&cmd, 1, status, NULL, 0, 0); -} - -/*--------------------end of SONY CDU535 very specific ---------------------*/ - -/* Convert from an integer 0-99 to BCD */ -static inline unsigned int -int_to_bcd(unsigned int val) -{ - int retval; - - retval = (val / 10) << 4; - retval = retval | val % 10; - return retval; -} - - -/* Convert from BCD to an integer from 0-99 */ -static unsigned int -bcd_to_int(unsigned int bcd) -{ - return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f); -} - - -/* - * Convert a logical sector value (like the OS would want to use for - * a block device) to an MSF format. - */ -static void -log_to_msf(unsigned int log, Byte *msf) -{ - log = log + LOG_START_OFFSET; - msf[0] = int_to_bcd(log / 4500); - log = log % 4500; - msf[1] = int_to_bcd(log / 75); - msf[2] = int_to_bcd(log % 75); -} - - -/* - * Convert an MSF format to a logical sector. - */ -static unsigned int -msf_to_log(Byte *msf) -{ - unsigned int log; - - - log = bcd_to_int(msf[2]); - log += bcd_to_int(msf[1]) * 75; - log += bcd_to_int(msf[0]) * 4500; - log = log - LOG_START_OFFSET; - - return log; -} - - -/* - * Take in integer size value and put it into a buffer like - * the drive would want to see a number-of-sector value. - */ -static void -size_to_buf(unsigned int size, Byte *buf) -{ - buf[0] = size / 65536; - size = size % 65536; - buf[1] = size / 256; - buf[2] = size % 256; -} - - -/* - * The OS calls this to perform a read or write operation to the drive. - * Write obviously fail. Reads to a read ahead of sony_buffer_size - * bytes to help speed operations. This especially helps since the OS - * may use 1024 byte blocks and the drive uses 2048 byte blocks. Since most - * data access on a CD is done sequentially, this saves a lot of operations. - */ -static void -do_cdu535_request(request_queue_t * q) -{ - struct request *req; - unsigned int read_size; - int block; - int nsect; - int copyoff; - int spin_up_retry; - Byte params[10]; - Byte status[2]; - Byte cmd[2]; - - while (1) { - req = elv_next_request(q); - if (!req) - return; - - block = req->sector; - nsect = req->nr_sectors; - if (!blk_fs_request(req)) { - end_request(req, 0); - continue; - } - if (rq_data_dir(req) == WRITE) { - end_request(req, 0); - continue; - } - /* - * If the block address is invalid or the request goes beyond - * the end of the media, return an error. - */ - if (sony_toc->lead_out_start_lba <= (block/4)) { - end_request(req, 0); - return; - } - if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) { - end_request(req, 0); - return; - } - while (0 < nsect) { - /* - * If the requested sector is not currently in - * the read-ahead buffer, it must be read in. - */ - if ((block < sony_first_block) || (sony_last_block < block)) { - sony_first_block = (block / 4) * 4; - log_to_msf(block / 4, params); - - /* - * If the full read-ahead would go beyond the end of the media, trim - * it back to read just till the end of the media. - */ - if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) { - sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1; - read_size = sony_toc->lead_out_start_lba - (block / 4); - } else { - sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1; - read_size = sony_buffer_sectors; - } - size_to_buf(read_size, ¶ms[3]); - - /* - * Read the data. If the drive was not spinning, - * spin it up and try some more. - */ - for (spin_up_retry=0 ;; ++spin_up_retry) { - /* This loop has been modified to support the Sony - * CDU-510/515 series, thanks to Claudio Porfiri - * <C.Porfiri@nisms.tei.ericsson.se>. - */ - /* - * This part is to deal with very slow hardware. We - * try at most MAX_SPINUP_RETRY times to read the same - * block. A check for seek_and_read_N_blocks' result is - * performed; if the result is wrong, the CDROM's engine - * is restarted and the operation is tried again. - */ - /* - * 1995-06-01: The system got problems when downloading - * from Slackware CDROM, the problem seems to be: - * seek_and_read_N_blocks returns BAD_STATUS and we - * should wait for a while before retrying, so a new - * part was added to discriminate the return value from - * seek_and_read_N_blocks for the various cases. - */ - int readStatus = seek_and_read_N_blocks(params, read_size, - status, sony_buffer, (read_size * CDU535_BLOCK_SIZE)); - if (0 <= readStatus) /* Good data; common case, placed first */ - break; - if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) { - /* give up */ - if (readStatus == NO_ROOM) - printk(CDU535_MESSAGE_NAME " No room to read from CD\n"); - else - printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n", - status[0]); - sony_first_block = -1; - sony_last_block = -1; - end_request(req, 0); - return; - } - if (readStatus == BAD_STATUS) { - /* Sleep for a while, then retry */ - set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(&sonycd535_lock); - schedule_timeout(RETRY_FOR_BAD_STATUS*HZ/10); - spin_lock_irq(&sonycd535_lock); - } -#if DEBUG > 0 - printk(CDU535_MESSAGE_NAME - " debug: calling spin up when reading data!\n"); -#endif - cmd[0] = SONY535_SPIN_UP; - do_sony_cmd(cmd, 1, status, NULL, 0, 0); - } - } - /* - * The data is in memory now, copy it to the buffer and advance to the - * next block to read. - */ - copyoff = block - sony_first_block; - memcpy(req->buffer, - sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512); - - block += 1; - nsect -= 1; - req->buffer += 512; - } - - end_request(req, 1); - } -} - -/* - * Read the table of contents from the drive and set sony_toc_read if - * successful. - */ -static void -sony_get_toc(void) -{ - Byte status[2]; - if (!sony_toc_read) { - /* do not call check_drive_status() from here since it can call this routine */ - if (request_toc_data(status, sony_toc) < 0) - return; - sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf); - sony_toc_read = 1; - } -} - - -/* - * Search for a specific track in the table of contents. track is - * passed in bcd format - */ -static int -find_track(int track) -{ - int i; - int num_tracks; - - - num_tracks = bcd_to_int(sony_toc->last_track_num) - - bcd_to_int(sony_toc->first_track_num) + 1; - for (i = 0; i < num_tracks; i++) { - if (sony_toc->tracks[i].track == track) { - return i; - } - } - - return -1; -} - -/* - * Read the subcode and put it int last_sony_subcode for future use. - */ -static int -read_subcode(void) -{ - Byte cmd = SONY535_REQUEST_SUB_Q_DATA; - Byte status[2]; - int dsc_status; - - if (check_drive_status() != 0) - return -EIO; - - if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode, - sizeof(struct s535_sony_subcode), 1)) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n", - status[0], dsc_status); - return -EIO; - } - return 0; -} - - -/* - * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If - * the drive is playing, the subchannel needs to be read (since it would be - * changing). If the drive is paused or completed, the subcode information has - * already been stored, just use that. The ioctl call wants things in decimal - * (not BCD), so all the conversions are done. - */ -static int -sony_get_subchnl_info(void __user *arg) -{ - struct cdrom_subchnl schi; - - /* Get attention stuff */ - if (check_drive_status() != 0) - return -EIO; - - sony_get_toc(); - if (!sony_toc_read) { - return -EIO; - } - if (copy_from_user(&schi, arg, sizeof schi)) - return -EFAULT; - - switch (sony_audio_status) { - case CDROM_AUDIO_PLAY: - if (read_subcode() < 0) { - return -EIO; - } - break; - - case CDROM_AUDIO_PAUSED: - case CDROM_AUDIO_COMPLETED: - break; - - case CDROM_AUDIO_NO_STATUS: - schi.cdsc_audiostatus = sony_audio_status; - if (copy_to_user(arg, &schi, sizeof schi)) - return -EFAULT; - return 0; - break; - - case CDROM_AUDIO_INVALID: - case CDROM_AUDIO_ERROR: - default: - return -EIO; - } - - schi.cdsc_audiostatus = sony_audio_status; - schi.cdsc_adr = last_sony_subcode->address; - schi.cdsc_ctrl = last_sony_subcode->control; - schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num); - schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num); - if (schi.cdsc_format == CDROM_MSF) { - schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]); - schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]); - schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]); - - schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]); - schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]); - schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]); - } else if (schi.cdsc_format == CDROM_LBA) { - schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf); - schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf); - } - return copy_to_user(arg, &schi, sizeof schi) ? -EFAULT : 0; -} - - -/* - * The big ugly ioctl handler. - */ -static int -cdu_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) -{ - Byte status[2]; - Byte cmd_buff[10], params[10]; - int i; - int dsc_status; - void __user *argp = (void __user *)arg; - - if (check_drive_status() != 0) - return -EIO; - - switch (cmd) { - case CDROMSTART: /* Spin up the drive */ - if (spin_up_drive(status) < 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n", - status[0]); - return -EIO; - } - return 0; - break; - - case CDROMSTOP: /* Spin down the drive */ - cmd_buff[0] = SONY535_HOLD; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - - /* - * Spin the drive down, ignoring the error if the disk was - * already not spinning. - */ - sony_audio_status = CDROM_AUDIO_NO_STATUS; - cmd_buff[0] = SONY535_SPIN_DOWN; - dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) || - ((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n", - status[0]); - return -EIO; - } - return 0; - break; - - case CDROMPAUSE: /* Pause the drive */ - cmd_buff[0] = SONY535_HOLD; /* CDU-31 driver uses AUDIO_STOP, not pause */ - if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n", - status[0]); - return -EIO; - } - /* Get the current position and save it for resuming */ - if (read_subcode() < 0) { - return -EIO; - } - cur_pos_msf[0] = last_sony_subcode->abs_msf[0]; - cur_pos_msf[1] = last_sony_subcode->abs_msf[1]; - cur_pos_msf[2] = last_sony_subcode->abs_msf[2]; - sony_audio_status = CDROM_AUDIO_PAUSED; - return 0; - break; - - case CDROMRESUME: /* Start the drive after being paused */ - set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); - - if (sony_audio_status != CDROM_AUDIO_PAUSED) { - return -EINVAL; - } - spin_up_drive(status); - - /* Start the drive at the saved position. */ - cmd_buff[0] = SONY535_PLAY_AUDIO; - cmd_buff[1] = 0; /* play back starting at this address */ - cmd_buff[2] = cur_pos_msf[0]; - cmd_buff[3] = cur_pos_msf[1]; - cmd_buff[4] = cur_pos_msf[2]; - cmd_buff[5] = SONY535_PLAY_AUDIO; - cmd_buff[6] = 2; /* set ending address */ - cmd_buff[7] = final_pos_msf[0]; - cmd_buff[8] = final_pos_msf[1]; - cmd_buff[9] = final_pos_msf[2]; - if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || - (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n", - status[0]); - return -EIO; - } - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - break; - - case CDROMPLAYMSF: /* Play starting at the given MSF address. */ - if (copy_from_user(params, argp, 6)) - return -EFAULT; - spin_up_drive(status); - set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); - - /* The parameters are given in int, must be converted */ - for (i = 0; i < 3; i++) { - cmd_buff[2 + i] = int_to_bcd(params[i]); - cmd_buff[7 + i] = int_to_bcd(params[i + 3]); - } - cmd_buff[0] = SONY535_PLAY_AUDIO; - cmd_buff[1] = 0; /* play back starting at this address */ - /* cmd_buff[2-4] are filled in for loop above */ - cmd_buff[5] = SONY535_PLAY_AUDIO; - cmd_buff[6] = 2; /* set ending address */ - /* cmd_buff[7-9] are filled in for loop above */ - if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || - (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n", - status[0]); - return -EIO; - } - /* Save the final position for pauses and resumes */ - final_pos_msf[0] = cmd_buff[7]; - final_pos_msf[1] = cmd_buff[8]; - final_pos_msf[2] = cmd_buff[9]; - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - break; - - case CDROMREADTOCHDR: /* Read the table of contents header */ - { - struct cdrom_tochdr __user *hdr = argp; - struct cdrom_tochdr loc_hdr; - - sony_get_toc(); - if (!sony_toc_read) - return -EIO; - loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num); - loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num); - if (copy_to_user(hdr, &loc_hdr, sizeof *hdr)) - return -EFAULT; - } - return 0; - break; - - case CDROMREADTOCENTRY: /* Read a given table of contents entry */ - { - struct cdrom_tocentry __user *entry = argp; - struct cdrom_tocentry loc_entry; - int track_idx; - Byte *msf_val = NULL; - - sony_get_toc(); - if (!sony_toc_read) { - return -EIO; - } - - if (copy_from_user(&loc_entry, entry, sizeof loc_entry)) - return -EFAULT; - - /* Lead out is handled separately since it is special. */ - if (loc_entry.cdte_track == CDROM_LEADOUT) { - loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ; - loc_entry.cdte_ctrl = sony_toc->control2; - msf_val = sony_toc->lead_out_start_msf; - } else { - track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); - if (track_idx < 0) - return -EINVAL; - loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address */ ; - loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control; - msf_val = sony_toc->tracks[track_idx].track_start_msf; - } - - /* Logical buffer address or MSF format requested? */ - if (loc_entry.cdte_format == CDROM_LBA) { - loc_entry.cdte_addr.lba = msf_to_log(msf_val); - } else if (loc_entry.cdte_format == CDROM_MSF) { - loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); - loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val + 1)); - loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val + 2)); - } - if (copy_to_user(entry, &loc_entry, sizeof *entry)) - return -EFAULT; - } - return 0; - break; - - case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - { - struct cdrom_ti ti; - int track_idx; - - sony_get_toc(); - if (!sony_toc_read) - return -EIO; - - if (copy_from_user(&ti, argp, sizeof ti)) - return -EFAULT; - if ((ti.cdti_trk0 < sony_toc->first_track_num) - || (sony_toc->last_track_num < ti.cdti_trk0) - || (ti.cdti_trk1 < ti.cdti_trk0)) { - return -EINVAL; - } - track_idx = find_track(int_to_bcd(ti.cdti_trk0)); - if (track_idx < 0) - return -EINVAL; - params[1] = sony_toc->tracks[track_idx].track_start_msf[0]; - params[2] = sony_toc->tracks[track_idx].track_start_msf[1]; - params[3] = sony_toc->tracks[track_idx].track_start_msf[2]; - /* - * If we want to stop after the last track, use the lead-out - * MSF to do that. - */ - if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) { - log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1, - &(params[4])); - } else { - track_idx = find_track(int_to_bcd(ti.cdti_trk1 + 1)); - if (track_idx < 0) - return -EINVAL; - log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf) - 1, - &(params[4])); - } - params[0] = 0x03; - - spin_up_drive(status); - - set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); - - /* Start the drive at the saved position. */ - cmd_buff[0] = SONY535_PLAY_AUDIO; - cmd_buff[1] = 0; /* play back starting at this address */ - cmd_buff[2] = params[1]; - cmd_buff[3] = params[2]; - cmd_buff[4] = params[3]; - cmd_buff[5] = SONY535_PLAY_AUDIO; - cmd_buff[6] = 2; /* set ending address */ - cmd_buff[7] = params[4]; - cmd_buff[8] = params[5]; - cmd_buff[9] = params[6]; - if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || - (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYTRKIND)\n", - status[0]); - printk("... Params: %x %x %x %x %x %x %x\n", - params[0], params[1], params[2], - params[3], params[4], params[5], params[6]); - return -EIO; - } - /* Save the final position for pauses and resumes */ - final_pos_msf[0] = params[4]; - final_pos_msf[1] = params[5]; - final_pos_msf[2] = params[6]; - sony_audio_status = CDROM_AUDIO_PLAY; - return 0; - } - - case CDROMSUBCHNL: /* Get subchannel info */ - return sony_get_subchnl_info(argp); - - case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ - { - struct cdrom_volctrl volctrl; - - if (copy_from_user(&volctrl, argp, sizeof volctrl)) - return -EFAULT; - cmd_buff[0] = SONY535_SET_VOLUME; - cmd_buff[1] = volctrl.channel0; - cmd_buff[2] = volctrl.channel1; - if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n", - status[0]); - return -EIO; - } - } - return 0; - - case CDROMEJECT: /* Eject the drive */ - cmd_buff[0] = SONY535_STOP; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - cmd_buff[0] = SONY535_SPIN_DOWN; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - - sony_audio_status = CDROM_AUDIO_INVALID; - cmd_buff[0] = SONY535_EJECT_CADDY; - if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n", - status[0]); - return -EIO; - } - return 0; - break; - - default: - return -EINVAL; - } -} - - -/* - * Open the drive for operations. Spin the drive up and read the table of - * contents if these have not already been done. - */ -static int -cdu_open(struct inode *inode, - struct file *filp) -{ - Byte status[2], cmd_buff[2]; - - if (sony_inuse) - return -EBUSY; - if (check_drive_status() != 0) - return -EIO; - sony_inuse = 1; - - if (spin_up_drive(status) != 0) { - printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n", - status[0]); - sony_inuse = 0; - return -EIO; - } - sony_get_toc(); - if (!sony_toc_read) { - cmd_buff[0] = SONY535_SPIN_DOWN; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); - sony_inuse = 0; - return -EIO; - } - check_disk_change(inode->i_bdev); - sony_usage++; - -#ifdef LOCK_DOORS - /* disable the eject button while mounted */ - cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON; - do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); -#endif - - return 0; -} - - -/* - * Close the drive. Spin it down if no task is using it. The spin - * down will fail if playing audio, so audio play is OK. - */ -static int -cdu_release(struct inode *inode, - struct file *filp) -{ - Byte status[2], cmd_no; - - sony_inuse = 0; - - if (0 < sony_usage) { - sony_usage--; - } - if (sony_usage == 0) { - check_drive_status(); - - if (sony_audio_status != CDROM_AUDIO_PLAY) { - cmd_no = SONY535_SPIN_DOWN; - do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0); - } -#ifdef LOCK_DOORS - /* enable the eject button after umount */ - cmd_no = SONY535_ENABLE_EJECT_BUTTON; - do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0); -#endif - } - return 0; -} - -static struct block_device_operations cdu_fops = -{ - .owner = THIS_MODULE, - .open = cdu_open, - .release = cdu_release, - .ioctl = cdu_ioctl, - .media_changed = cdu535_check_media_change, -}; - -static struct gendisk *cdu_disk; - -/* - * Initialize the driver. - */ -static int __init sony535_init(void) -{ - struct s535_sony_drive_config drive_config; - Byte cmd_buff[3]; - Byte ret_buff[2]; - Byte status[2]; - unsigned long snap; - int got_result = 0; - int tmp_irq; - int i; - int err; - - /* Setting the base I/O address to 0 will disable it. */ - if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0)) - return 0; - - /* Set up all the register locations */ - result_reg = sony535_cd_base_io; - command_reg = sony535_cd_base_io; - data_reg = sony535_cd_base_io + 1; - read_status_reg = sony535_cd_base_io + 2; - select_unit_reg = sony535_cd_base_io + 3; - -#ifndef USE_IRQ - sony535_irq_used = 0; /* polling only until this is ready... */ -#endif - /* we need to poll until things get initialized */ - tmp_irq = sony535_irq_used; - sony535_irq_used = 0; - -#if DEBUG > 0 - printk(KERN_INFO CDU535_MESSAGE_NAME ": probing base address %03X\n", - sony535_cd_base_io); -#endif - /* look for the CD-ROM, follows the procedure in the DOS driver */ - inb(select_unit_reg); - /* wait for 40 18 Hz ticks (reverse-engineered from DOS driver) */ - schedule_timeout_interruptible((HZ+17)*40/18); - inb(result_reg); - - outb(0, read_status_reg); /* does a reset? */ - snap = jiffies; - while (jiffies-snap < SONY_JIFFIES_TIMEOUT) { - select_unit(0); - if (inb(result_reg) != 0xff) { - got_result = 1; - break; - } - sony_sleep(); - } - - if (!got_result || check_drive_status() == TIME_OUT) - goto Enodev; - - /* CD-ROM drive responded -- get the drive configuration */ - cmd_buff[0] = SONY535_INQUIRY; - if (do_sony_cmd(cmd_buff, 1, status, (Byte *)&drive_config, 28, 1) != 0) - goto Enodev; - - /* was able to get the configuration, - * set drive mode as rest of init - */ -#if DEBUG > 0 - /* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */ - if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 ) - printk(CDU535_MESSAGE_NAME - "Inquiry command returned status = 0x%x\n", status[0]); -#endif - /* now ready to use interrupts, if available */ - sony535_irq_used = tmp_irq; - - /* A negative sony535_irq_used will attempt an autoirq. */ - if (sony535_irq_used < 0) { - unsigned long irq_mask, delay; - - irq_mask = probe_irq_on(); - enable_interrupts(); - outb(0, read_status_reg); /* does a reset? */ - delay = jiffies + HZ/10; - while (time_before(jiffies, delay)) ; - - sony535_irq_used = probe_irq_off(irq_mask); - disable_interrupts(); - } - if (sony535_irq_used > 0) { - if (request_irq(sony535_irq_used, cdu535_interrupt, - IRQF_DISABLED, CDU535_HANDLE, NULL)) { - printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME - " driver; polling instead.\n", sony535_irq_used); - sony535_irq_used = 0; - } - } - cmd_buff[0] = SONY535_SET_DRIVE_MODE; - cmd_buff[1] = 0x0; /* default audio */ - if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) != 0) - goto Enodev_irq; - - /* set the drive mode successful, we are set! */ - sony_buffer_size = SONY535_BUFFER_SIZE; - sony_buffer_sectors = sony_buffer_size / CDU535_BLOCK_SIZE; - - printk(KERN_INFO CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s", - drive_config.vendor_id, - drive_config.product_id, - drive_config.product_rev_level); - printk(" base address %03X, ", sony535_cd_base_io); - if (tmp_irq > 0) - printk("IRQ%d, ", tmp_irq); - printk("using %d byte buffer\n", sony_buffer_size); - - if (register_blkdev(MAJOR_NR, CDU535_HANDLE)) { - err = -EIO; - goto out1; - } - sonycd535_queue = blk_init_queue(do_cdu535_request, &sonycd535_lock); - if (!sonycd535_queue) { - err = -ENOMEM; - goto out1a; - } - - blk_queue_hardsect_size(sonycd535_queue, CDU535_BLOCK_SIZE); - sony_toc = kmalloc(sizeof(struct s535_sony_toc), GFP_KERNEL); - err = -ENOMEM; - if (!sony_toc) - goto out2; - last_sony_subcode = kmalloc(sizeof(struct s535_sony_subcode), GFP_KERNEL); - if (!last_sony_subcode) - goto out3; - sony_buffer = kmalloc(sizeof(Byte *) * sony_buffer_sectors, GFP_KERNEL); - if (!sony_buffer) - goto out4; - for (i = 0; i < sony_buffer_sectors; i++) { - sony_buffer[i] = kmalloc(CDU535_BLOCK_SIZE, GFP_KERNEL); - if (!sony_buffer[i]) { - while (--i>=0) - kfree(sony_buffer[i]); - goto out5; - } - } - initialized = 1; - - cdu_disk = alloc_disk(1); - if (!cdu_disk) - goto out6; - cdu_disk->major = MAJOR_NR; - cdu_disk->first_minor = 0; - cdu_disk->fops = &cdu_fops; - sprintf(cdu_disk->disk_name, "cdu"); - - if (!request_region(sony535_cd_base_io, 4, CDU535_HANDLE)) { - printk(KERN_WARNING"sonycd535: Unable to request region 0x%x\n", - sony535_cd_base_io); - goto out7; - } - cdu_disk->queue = sonycd535_queue; - add_disk(cdu_disk); - return 0; - -out7: - put_disk(cdu_disk); -out6: - for (i = 0; i < sony_buffer_sectors; i++) - kfree(sony_buffer[i]); -out5: - kfree(sony_buffer); -out4: - kfree(last_sony_subcode); -out3: - kfree(sony_toc); -out2: - blk_cleanup_queue(sonycd535_queue); -out1a: - unregister_blkdev(MAJOR_NR, CDU535_HANDLE); -out1: - if (sony535_irq_used) - free_irq(sony535_irq_used, NULL); - return err; -Enodev_irq: - if (sony535_irq_used) - free_irq(sony535_irq_used, NULL); -Enodev: - printk("Did not find a " CDU535_MESSAGE_NAME " drive\n"); - return -EIO; -} - -#ifndef MODULE - -/* - * accept "kernel command line" parameters - * (added by emoenke@gwdg.de) - * - * use: tell LILO: - * sonycd535=0x320 - * - * the address value has to be the existing CDROM port address. - */ -static int __init -sonycd535_setup(char *strings) -{ - int ints[3]; - (void)get_options(strings, ARRAY_SIZE(ints), ints); - /* if IRQ change and default io base desired, - * then call with io base of 0 - */ - if (ints[0] > 0) - if (ints[1] != 0) - sony535_cd_base_io = ints[1]; - if (ints[0] > 1) - sony535_irq_used = ints[2]; - if ((strings != NULL) && (*strings != '\0')) - printk(CDU535_MESSAGE_NAME - ": Warning: Unknown interface type: %s\n", strings); - - return 1; -} - -__setup("sonycd535=", sonycd535_setup); - -#endif /* MODULE */ - -static void __exit -sony535_exit(void) -{ - int i; - - release_region(sony535_cd_base_io, 4); - for (i = 0; i < sony_buffer_sectors; i++) - kfree(sony_buffer[i]); - kfree(sony_buffer); - kfree(last_sony_subcode); - kfree(sony_toc); - del_gendisk(cdu_disk); - put_disk(cdu_disk); - blk_cleanup_queue(sonycd535_queue); - if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL) - printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n"); - else - printk(KERN_INFO CDU535_HANDLE " module released\n"); -} - -module_init(sony535_init); -module_exit(sony535_exit); - - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_BLOCKDEV_MAJOR(CDU535_CDROM_MAJOR); diff --git a/drivers/cdrom/sonycd535.h b/drivers/cdrom/sonycd535.h deleted file mode 100644 index 5dea1ef168d..00000000000 --- a/drivers/cdrom/sonycd535.h +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef SONYCD535_H -#define SONYCD535_H - -/* - * define all the commands recognized by the CDU-531/5 - */ -#define SONY535_REQUEST_DRIVE_STATUS_1 (0x80) -#define SONY535_REQUEST_SENSE (0x82) -#define SONY535_REQUEST_DRIVE_STATUS_2 (0x84) -#define SONY535_REQUEST_ERROR_STATUS (0x86) -#define SONY535_REQUEST_AUDIO_STATUS (0x88) -#define SONY535_INQUIRY (0x8a) - -#define SONY535_SET_INACTIVITY_TIME (0x90) - -#define SONY535_SEEK_AND_READ_N_BLOCKS_1 (0xa0) -#define SONY535_SEEK_AND_READ_N_BLOCKS_2 (0xa4) -#define SONY535_PLAY_AUDIO (0xa6) - -#define SONY535_REQUEST_DISC_CAPACITY (0xb0) -#define SONY535_REQUEST_TOC_DATA (0xb2) -#define SONY535_REQUEST_SUB_Q_DATA (0xb4) -#define SONY535_REQUEST_ISRC (0xb6) -#define SONY535_REQUEST_UPC_EAN (0xb8) - -#define SONY535_SET_DRIVE_MODE (0xc0) -#define SONY535_REQUEST_DRIVE_MODE (0xc2) -#define SONY535_SET_RETRY_COUNT (0xc4) - -#define SONY535_DIAGNOSTIC_1 (0xc6) -#define SONY535_DIAGNOSTIC_4 (0xcc) -#define SONY535_DIAGNOSTIC_5 (0xce) - -#define SONY535_EJECT_CADDY (0xd0) -#define SONY535_DISABLE_EJECT_BUTTON (0xd2) -#define SONY535_ENABLE_EJECT_BUTTON (0xd4) - -#define SONY535_HOLD (0xe0) -#define SONY535_AUDIO_PAUSE_ON_OFF (0xe2) -#define SONY535_SET_VOLUME (0xe8) - -#define SONY535_STOP (0xf0) -#define SONY535_SPIN_UP (0xf2) -#define SONY535_SPIN_DOWN (0xf4) - -#define SONY535_CLEAR_PARAMETERS (0xf6) -#define SONY535_CLEAR_ENDING_ADDRESS (0xf8) - -/* - * define some masks - */ -#define SONY535_DATA_NOT_READY_BIT (0x1) -#define SONY535_RESULT_NOT_READY_BIT (0x2) - -/* - * drive status 1 - */ -#define SONY535_STATUS1_COMMAND_ERROR (0x1) -#define SONY535_STATUS1_DATA_ERROR (0x2) -#define SONY535_STATUS1_SEEK_ERROR (0x4) -#define SONY535_STATUS1_DISC_TYPE_ERROR (0x8) -#define SONY535_STATUS1_NOT_SPINNING (0x10) -#define SONY535_STATUS1_EJECT_BUTTON_PRESSED (0x20) -#define SONY535_STATUS1_CADDY_NOT_INSERTED (0x40) -#define SONY535_STATUS1_BYTE_TWO_FOLLOWS (0x80) - -/* - * drive status 2 - */ -#define SONY535_CDD_LOADING_ERROR (0x7) -#define SONY535_CDD_NO_DISC (0x8) -#define SONY535_CDD_UNLOADING_ERROR (0x9) -#define SONY535_CDD_CADDY_NOT_INSERTED (0xd) -#define SONY535_ATN_RESET_OCCURRED (0x2) -#define SONY535_ATN_DISC_CHANGED (0x4) -#define SONY535_ATN_RESET_AND_DISC_CHANGED (0x6) -#define SONY535_ATN_EJECT_IN_PROGRESS (0xe) -#define SONY535_ATN_BUSY (0xf) - -/* - * define some parameters - */ -#define SONY535_AUDIO_DRIVE_MODE (0) -#define SONY535_CDROM_DRIVE_MODE (0xe0) - -#define SONY535_PLAY_OP_PLAYBACK (0) -#define SONY535_PLAY_OP_ENTER_HOLD (1) -#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR (2) -#define SONY535_PLAY_OP_SCAN_FORWARD (3) -#define SONY535_PLAY_OP_SCAN_BACKWARD (4) - -/* - * convert from msf format to block number - */ -#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f)) -#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2]) - -/* - * error return values from the doSonyCmd() routines - */ -#define TIME_OUT (-1) -#define NO_CDROM (-2) -#define BAD_STATUS (-3) -#define CD_BUSY (-4) -#define NOT_DATA_CD (-5) -#define NO_ROOM (-6) - -#define LOG_START_OFFSET 150 /* Offset of first logical sector */ - -#define SONY_JIFFIES_TIMEOUT (5*HZ) /* Maximum time - the drive will wait/try for an - operation */ -#define SONY_READY_RETRIES (50000) /* How many times to retry a - spin waiting for a register - to come ready */ -#define SONY535_FAST_POLLS (10000) /* how many times recheck - status waiting for a data - to become ready */ - -typedef unsigned char Byte; - -/* - * This is the complete status returned from the drive configuration request - * command. - */ -struct s535_sony_drive_config -{ - char vendor_id[8]; - char product_id[16]; - char product_rev_level[4]; -}; - -/* The following is returned from the request sub-q data command */ -struct s535_sony_subcode -{ - unsigned char address :4; - unsigned char control :4; - unsigned char track_num; - unsigned char index_num; - unsigned char rel_msf[3]; - unsigned char abs_msf[3]; -}; - -struct s535_sony_disc_capacity -{ - Byte mFirstTrack, sFirstTrack, fFirstTrack; - Byte mLeadOut, sLeadOut, fLeadOut; -}; - -/* - * The following is returned from the request TOC (Table Of Contents) command. - * (last_track_num-first_track_num+1) values are valid in tracks. - */ -struct s535_sony_toc -{ - unsigned char reserved0 :4; - unsigned char control0 :4; - unsigned char point0; - unsigned char first_track_num; - unsigned char reserved0a; - unsigned char reserved0b; - unsigned char reserved1 :4; - unsigned char control1 :4; - unsigned char point1; - unsigned char last_track_num; - unsigned char dummy1; - unsigned char dummy2; - unsigned char reserved2 :4; - unsigned char control2 :4; - unsigned char point2; - unsigned char lead_out_start_msf[3]; - struct - { - unsigned char reserved :4; - unsigned char control :4; - unsigned char track; - unsigned char track_start_msf[3]; - } tracks[100]; - - unsigned int lead_out_start_lba; -}; - -#endif /* SONYCD535_H */ diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 1b094509b1d..90965b4def5 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -1005,8 +1005,8 @@ static const unsigned short x86_keycodes[256] = 284,285,309, 0,312, 91,327,328,329,331,333,335,336,337,338,339, 367,288,302,304,350, 89,334,326,267,126,268,269,125,347,348,349, 360,261,262,263,268,376,100,101,321,316,373,286,289,102,351,355, - 103,104,105,275,287,279,306,106,274,107,294,364,358,363,362,361, - 291,108,381,281,290,272,292,305,280, 99,112,257,258,359,113,114, + 103,104,105,275,287,279,258,106,274,107,294,364,358,363,362,361, + 291,108,381,281,290,272,292,305,280, 99,112,257,306,359,113,114, 264,117,271,374,379,265,266, 93, 94, 95, 85,259,375,260, 90,116, 377,109,111,277,278,282,283,295,296,297,299,300,301,293,303,307, 308,310,313,314,315,317,318,319,320,357,322,323,324,325,276,330, diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 7e082bceed5..bbee97ff355 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -24,7 +24,7 @@ #include <linux/crash_dump.h> #include <linux/backing-dev.h> #include <linux/bootmem.h> -#include <linux/pipe_fs_i.h> +#include <linux/splice.h> #include <linux/pfn.h> #include <asm/uaccess.h> diff --git a/drivers/ide/legacy/hd.c b/drivers/ide/legacy/hd.c index 661c12f6dda..7f4c0a5050a 100644 --- a/drivers/ide/legacy/hd.c +++ b/drivers/ide/legacy/hd.c @@ -623,7 +623,8 @@ repeat: cyl = track / disk->head; #ifdef DEBUG printk("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=%p\n", - req->rq_disk->disk_name, (req->cmd == READ)?"read":"writ", + req->rq_disk->disk_name, + req_data_dir(req) == READ ? "read" : "writ", cyl, head, sec, nsect, req->buffer); #endif if (blk_fs_request(req)) { diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index be6b93c20f6..ab4b2d9b532 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -30,6 +30,7 @@ struct evdev { wait_queue_head_t wait; struct evdev_client *grab; struct list_head client_list; + struct device dev; }; struct evdev_client { @@ -94,8 +95,10 @@ static int evdev_flush(struct file *file, fl_owner_t id) return input_flush_device(&evdev->handle, file); } -static void evdev_free(struct evdev *evdev) +static void evdev_free(struct device *dev) { + struct evdev *evdev = container_of(dev, struct evdev, dev); + evdev_table[evdev->minor] = NULL; kfree(evdev); } @@ -114,12 +117,10 @@ static int evdev_release(struct inode *inode, struct file *file) list_del(&client->node); kfree(client); - if (!--evdev->open) { - if (evdev->exist) - input_close_device(&evdev->handle); - else - evdev_free(evdev); - } + if (!--evdev->open && evdev->exist) + input_close_device(&evdev->handle); + + put_device(&evdev->dev); return 0; } @@ -139,24 +140,32 @@ static int evdev_open(struct inode *inode, struct file *file) if (!evdev || !evdev->exist) return -ENODEV; + get_device(&evdev->dev); + client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); - if (!client) - return -ENOMEM; + if (!client) { + error = -ENOMEM; + goto err_put_evdev; + } client->evdev = evdev; list_add_tail(&client->node, &evdev->client_list); if (!evdev->open++ && evdev->exist) { error = input_open_device(&evdev->handle); - if (error) { - list_del(&client->node); - kfree(client); - return error; - } + if (error) + goto err_free_client; } file->private_data = client; return 0; + + err_free_client: + list_del(&client->node); + kfree(client); + err_put_evdev: + put_device(&evdev->dev); + return error; } #ifdef CONFIG_COMPAT @@ -625,8 +634,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; - struct class_device *cdev; - dev_t devt; int minor; int error; @@ -649,38 +656,32 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, evdev->handle.name = evdev->name; evdev->handle.handler = handler; evdev->handle.private = evdev; - sprintf(evdev->name, "event%d", minor); - - evdev_table[minor] = evdev; + snprintf(evdev->name, sizeof(evdev->name), "event%d", minor); - devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), + snprintf(evdev->dev.bus_id, sizeof(evdev->dev.bus_id), + "event%d", minor); + evdev->dev.class = &input_class; + evdev->dev.parent = &dev->dev; + evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); + evdev->dev.release = evdev_free; + device_initialize(&evdev->dev); - cdev = class_device_create(&input_class, &dev->cdev, devt, - dev->cdev.dev, evdev->name); - if (IS_ERR(cdev)) { - error = PTR_ERR(cdev); - goto err_free_evdev; - } + evdev_table[minor] = evdev; - /* temporary symlink to keep userspace happy */ - error = sysfs_create_link(&input_class.subsys.kobj, - &cdev->kobj, evdev->name); + error = device_add(&evdev->dev); if (error) - goto err_cdev_destroy; + goto err_free_evdev; error = input_register_handle(&evdev->handle); if (error) - goto err_remove_link; + goto err_delete_evdev; return 0; - err_remove_link: - sysfs_remove_link(&input_class.subsys.kobj, evdev->name); - err_cdev_destroy: - class_device_destroy(&input_class, devt); + err_delete_evdev: + device_del(&evdev->dev); err_free_evdev: - kfree(evdev); - evdev_table[minor] = NULL; + put_device(&evdev->dev); return error; } @@ -690,10 +691,8 @@ static void evdev_disconnect(struct input_handle *handle) struct evdev_client *client; input_unregister_handle(handle); + device_del(&evdev->dev); - sysfs_remove_link(&input_class.subsys.kobj, evdev->name); - class_device_destroy(&input_class, - MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor)); evdev->exist = 0; if (evdev->open) { @@ -702,8 +701,9 @@ static void evdev_disconnect(struct input_handle *handle) list_for_each_entry(client, &evdev->client_list, node) kill_fasync(&client->fasync, SIGIO, POLL_HUP); wake_up_interruptible(&evdev->wait); - } else - evdev_free(evdev); + } + + put_device(&evdev->dev); } static const struct input_device_id evdev_ids[] = { diff --git a/drivers/input/input.c b/drivers/input/input.c index ccd8abafcb7..75b4d2a83dd 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -442,7 +442,7 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han printk(KERN_ERR "input: failed to attach handler %s to device %s, " "error: %d\n", - handler->name, kobject_name(&dev->cdev.kobj), error); + handler->name, kobject_name(&dev->dev.kobj), error); return error; } @@ -527,7 +527,7 @@ static void input_seq_print_bitmap(struct seq_file *seq, const char *name, static int input_devices_seq_show(struct seq_file *seq, void *v) { struct input_dev *dev = container_of(v, struct input_dev, node); - const char *path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); + const char *path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); struct input_handle *handle; seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", @@ -682,15 +682,17 @@ static inline int input_proc_init(void) { return 0; } static inline void input_proc_exit(void) { } #endif -#define INPUT_DEV_STRING_ATTR_SHOW(name) \ -static ssize_t input_dev_show_##name(struct class_device *dev, char *buf) \ -{ \ - struct input_dev *input_dev = to_input_dev(dev); \ - \ - return scnprintf(buf, PAGE_SIZE, "%s\n", \ - input_dev->name ? input_dev->name : ""); \ -} \ -static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL); +#define INPUT_DEV_STRING_ATTR_SHOW(name) \ +static ssize_t input_dev_show_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + \ + return scnprintf(buf, PAGE_SIZE, "%s\n", \ + input_dev->name ? input_dev->name : ""); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL) INPUT_DEV_STRING_ATTR_SHOW(name); INPUT_DEV_STRING_ATTR_SHOW(phys); @@ -744,7 +746,9 @@ static int input_print_modalias(char *buf, int size, struct input_dev *id, return len; } -static ssize_t input_dev_show_modalias(struct class_device *dev, char *buf) +static ssize_t input_dev_show_modalias(struct device *dev, + struct device_attribute *attr, + char *buf) { struct input_dev *id = to_input_dev(dev); ssize_t len; @@ -753,13 +757,13 @@ static ssize_t input_dev_show_modalias(struct class_device *dev, char *buf) return min_t(int, len, PAGE_SIZE); } -static CLASS_DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL); +static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL); static struct attribute *input_dev_attrs[] = { - &class_device_attr_name.attr, - &class_device_attr_phys.attr, - &class_device_attr_uniq.attr, - &class_device_attr_modalias.attr, + &dev_attr_name.attr, + &dev_attr_phys.attr, + &dev_attr_uniq.attr, + &dev_attr_modalias.attr, NULL }; @@ -767,13 +771,15 @@ static struct attribute_group input_dev_attr_group = { .attrs = input_dev_attrs, }; -#define INPUT_DEV_ID_ATTR(name) \ -static ssize_t input_dev_show_id_##name(struct class_device *dev, char *buf) \ -{ \ - struct input_dev *input_dev = to_input_dev(dev); \ - return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name); \ -} \ -static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL); +#define INPUT_DEV_ID_ATTR(name) \ +static ssize_t input_dev_show_id_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL) INPUT_DEV_ID_ATTR(bustype); INPUT_DEV_ID_ATTR(vendor); @@ -781,10 +787,10 @@ INPUT_DEV_ID_ATTR(product); INPUT_DEV_ID_ATTR(version); static struct attribute *input_dev_id_attrs[] = { - &class_device_attr_bustype.attr, - &class_device_attr_vendor.attr, - &class_device_attr_product.attr, - &class_device_attr_version.attr, + &dev_attr_bustype.attr, + &dev_attr_vendor.attr, + &dev_attr_product.attr, + &dev_attr_version.attr, NULL }; @@ -813,15 +819,17 @@ static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, return len; } -#define INPUT_DEV_CAP_ATTR(ev, bm) \ -static ssize_t input_dev_show_cap_##bm(struct class_device *dev, char *buf) \ -{ \ - struct input_dev *input_dev = to_input_dev(dev); \ - int len = input_print_bitmap(buf, PAGE_SIZE, \ - input_dev->bm##bit, ev##_MAX, 1); \ - return min_t(int, len, PAGE_SIZE); \ -} \ -static CLASS_DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL); +#define INPUT_DEV_CAP_ATTR(ev, bm) \ +static ssize_t input_dev_show_cap_##bm(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct input_dev *input_dev = to_input_dev(dev); \ + int len = input_print_bitmap(buf, PAGE_SIZE, \ + input_dev->bm##bit, ev##_MAX, 1); \ + return min_t(int, len, PAGE_SIZE); \ +} \ +static DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL) INPUT_DEV_CAP_ATTR(EV, ev); INPUT_DEV_CAP_ATTR(KEY, key); @@ -834,15 +842,15 @@ INPUT_DEV_CAP_ATTR(FF, ff); INPUT_DEV_CAP_ATTR(SW, sw); static struct attribute *input_dev_caps_attrs[] = { - &class_device_attr_ev.attr, - &class_device_attr_key.attr, - &class_device_attr_rel.attr, - &class_device_attr_abs.attr, - &class_device_attr_msc.attr, - &class_device_attr_led.attr, - &class_device_attr_snd.attr, - &class_device_attr_ff.attr, - &class_device_attr_sw.attr, + &dev_attr_ev.attr, + &dev_attr_key.attr, + &dev_attr_rel.attr, + &dev_attr_abs.attr, + &dev_attr_msc.attr, + &dev_attr_led.attr, + &dev_attr_snd.attr, + &dev_attr_ff.attr, + &dev_attr_sw.attr, NULL }; @@ -858,9 +866,9 @@ static struct attribute_group *input_dev_attr_groups[] = { NULL }; -static void input_dev_release(struct class_device *class_dev) +static void input_dev_release(struct device *device) { - struct input_dev *dev = to_input_dev(class_dev); + struct input_dev *dev = to_input_dev(device); input_ff_destroy(dev); kfree(dev); @@ -947,10 +955,10 @@ static int input_add_uevent_modalias_var(char **envp, int num_envp, int *cur_ind return err; \ } while (0) -static int input_dev_uevent(struct class_device *cdev, char **envp, +static int input_dev_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size) { - struct input_dev *dev = to_input_dev(cdev); + struct input_dev *dev = to_input_dev(device); int i = 0; int len = 0; @@ -988,10 +996,14 @@ static int input_dev_uevent(struct class_device *cdev, char **envp, return 0; } +static struct device_type input_dev_type = { + .groups = input_dev_attr_groups, + .release = input_dev_release, + .uevent = input_dev_uevent, +}; + struct class input_class = { - .name = "input", - .release = input_dev_release, - .uevent = input_dev_uevent, + .name = "input", }; EXPORT_SYMBOL_GPL(input_class); @@ -1010,9 +1022,9 @@ struct input_dev *input_allocate_device(void) dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); if (dev) { - dev->cdev.class = &input_class; - dev->cdev.groups = input_dev_attr_groups; - class_device_initialize(&dev->cdev); + dev->dev.type = &input_dev_type; + dev->dev.class = &input_class; + device_initialize(&dev->dev); mutex_init(&dev->mutex); INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); @@ -1131,17 +1143,17 @@ int input_register_device(struct input_dev *dev) list_add_tail(&dev->node, &input_dev_list); - snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id), + snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); - if (!dev->cdev.dev) - dev->cdev.dev = dev->dev.parent; + if (dev->cdev.dev) + dev->dev.parent = dev->cdev.dev; - error = class_device_add(&dev->cdev); + error = device_add(&dev->dev); if (error) return error; - path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); + path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); @@ -1173,7 +1185,7 @@ void input_unregister_device(struct input_dev *dev) list_del_init(&dev->node); - class_device_unregister(&dev->cdev); + device_unregister(&dev->dev); input_wakeup_procfs_readers(); } diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 10e3b7bc925..a9a0180bfd4 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -43,6 +43,8 @@ struct joydev { struct input_handle handle; wait_queue_head_t wait; struct list_head client_list; + struct device dev; + struct js_corr corr[ABS_MAX + 1]; struct JS_DATA_SAVE_TYPE glue; int nabs; @@ -138,8 +140,10 @@ static int joydev_fasync(int fd, struct file *file, int on) return retval < 0 ? retval : 0; } -static void joydev_free(struct joydev *joydev) +static void joydev_free(struct device *dev) { + struct joydev *joydev = container_of(dev, struct joydev, dev); + joydev_table[joydev->minor] = NULL; kfree(joydev); } @@ -154,12 +158,10 @@ static int joydev_release(struct inode *inode, struct file *file) list_del(&client->node); kfree(client); - if (!--joydev->open) { - if (joydev->exist) - input_close_device(&joydev->handle); - else - joydev_free(joydev); - } + if (!--joydev->open && joydev->exist) + input_close_device(&joydev->handle); + + put_device(&joydev->dev); return 0; } @@ -178,24 +180,32 @@ static int joydev_open(struct inode *inode, struct file *file) if (!joydev || !joydev->exist) return -ENODEV; + get_device(&joydev->dev); + client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); - if (!client) - return -ENOMEM; + if (!client) { + error = -ENOMEM; + goto err_put_joydev; + } client->joydev = joydev; list_add_tail(&client->node, &joydev->client_list); if (!joydev->open++ && joydev->exist) { error = input_open_device(&joydev->handle); - if (error) { - list_del(&client->node); - kfree(client); - return error; - } + if (error) + goto err_free_client; } file->private_data = client; return 0; + + err_free_client: + list_del(&client->node); + kfree(client); + err_put_joydev: + put_device(&joydev->dev); + return error; } static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) @@ -481,8 +491,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct joydev *joydev; - struct class_device *cdev; - dev_t devt; int i, j, t, minor; int error; @@ -505,7 +513,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, joydev->handle.name = joydev->name; joydev->handle.handler = handler; joydev->handle.private = joydev; - sprintf(joydev->name, "js%d", minor); + snprintf(joydev->name, sizeof(joydev->name), "js%d", minor); for (i = 0; i < ABS_MAX + 1; i++) if (test_bit(i, dev->absbit)) { @@ -547,36 +555,30 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); } - joydev_table[minor] = joydev; - - devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), + snprintf(joydev->dev.bus_id, sizeof(joydev->dev.bus_id), + "js%d", minor); + joydev->dev.class = &input_class; + joydev->dev.parent = &dev->dev; + joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); + joydev->dev.release = joydev_free; + device_initialize(&joydev->dev); - cdev = class_device_create(&input_class, &dev->cdev, devt, - dev->cdev.dev, joydev->name); - if (IS_ERR(cdev)) { - error = PTR_ERR(cdev); - goto err_free_joydev; - } + joydev_table[minor] = joydev; - /* temporary symlink to keep userspace happy */ - error = sysfs_create_link(&input_class.subsys.kobj, - &cdev->kobj, joydev->name); + error = device_add(&joydev->dev); if (error) - goto err_cdev_destroy; + goto err_free_joydev; error = input_register_handle(&joydev->handle); if (error) - goto err_remove_link; + goto err_delete_joydev; return 0; - err_remove_link: - sysfs_remove_link(&input_class.subsys.kobj, joydev->name); - err_cdev_destroy: - class_device_destroy(&input_class, devt); + err_delete_joydev: + device_del(&joydev->dev); err_free_joydev: - joydev_table[minor] = NULL; - kfree(joydev); + put_device(&joydev->dev); return error; } @@ -587,9 +589,8 @@ static void joydev_disconnect(struct input_handle *handle) struct joydev_client *client; input_unregister_handle(handle); + device_del(&joydev->dev); - sysfs_remove_link(&input_class.subsys.kobj, joydev->name); - class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor)); joydev->exist = 0; if (joydev->open) { @@ -597,8 +598,9 @@ static void joydev_disconnect(struct input_handle *handle) list_for_each_entry(client, &joydev->client_list, node) kill_fasync(&client->fasync, SIGIO, POLL_HUP); wake_up_interruptible(&joydev->wait); - } else - joydev_free(joydev); + } + + put_device(&joydev->dev); } static const struct input_device_id joydev_blacklist[] = { diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index b0023452ec9..12db72d83ea 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -268,4 +268,11 @@ config JOYSTICK_XPAD To compile this driver as a module, choose M here: the module will be called xpad. +config JOYSTICK_XPAD_FF + bool "X-Box gamepad rumble support" + depends on JOYSTICK_XPAD && INPUT + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you want to take advantage of xbox 360 rumble features. + endif diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c index 555319e6378..4ed3a3eadf1 100644 --- a/drivers/input/joystick/grip_mp.c +++ b/drivers/input/joystick/grip_mp.c @@ -320,10 +320,10 @@ static int multiport_io(struct gameport* gameport, int sendflags, int sendcode, static int dig_mode_start(struct gameport *gameport, u32 *packet) { - int i, seq_len = sizeof(init_seq)/sizeof(int); + int i; int flags, tries = 0, bads = 0; - for (i = 0; i < seq_len; i++) { /* Send magic sequence */ + for (i = 0; i < ARRAY_SIZE(init_seq); i++) { /* Send magic sequence */ if (init_seq[i]) gameport_trigger(gameport); udelay(GRIP_INIT_DELAY); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 8c8cd95a698..244089c5265 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -8,6 +8,7 @@ * Ivan Hawkes <blackhawk@ivanhawkes.com> * 2005 Dominic Cerquetti <binary1230@yahoo.com> * 2006 Adam Buchbinder <adam.buchbinder@gmail.com> + * 2007 Jan Kratochvil <honza@jikos.cz> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,6 +29,7 @@ * - information from http://euc.jp/periphs/xbox-controller.ja.html * - the iForce driver drivers/char/joystick/iforce.c * - the skeleton-driver drivers/usb/usb-skeleton.c + * - Xbox 360 information http://www.free60.org/wiki/Gamepad * * Thanks to: * - ITO Takayuki for providing essential xpad information on his website @@ -88,6 +90,9 @@ #define MAP_DPAD_TO_AXES 1 #define MAP_DPAD_UNKNOWN -1 +#define XTYPE_XBOX 0 +#define XTYPE_XBOX360 1 + static int dpad_to_buttons; module_param(dpad_to_buttons, bool, S_IRUGO); MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads"); @@ -97,40 +102,42 @@ static const struct xpad_device { u16 idProduct; char *name; u8 dpad_mapping; + u8 xtype; } xpad_device[] = { - { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES }, - { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES }, - { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES }, - { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES }, - { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS }, - { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES }, - { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES }, - { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES }, - { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES }, - { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES }, - { 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES }, - { 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES }, - { 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES }, - { 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES }, - { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS }, - { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES }, - { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS }, - { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES }, - { 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES }, - { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES }, - { 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES }, - { 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES}, - { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", MAP_DPAD_TO_AXES }, - { 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES }, - { 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES }, - { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES }, - { 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES }, - { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES }, - { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES }, - { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS }, - { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS }, - { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES }, - { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN } + { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, + { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_XBOX } }; static const signed short xpad_btn[] = { @@ -146,6 +153,12 @@ static const signed short xpad_btn_pad[] = { -1 /* terminating entry */ }; +static const signed short xpad360_btn[] = { /* buttons for x360 controller */ + BTN_TL, BTN_TR, /* Button LB/RB */ + BTN_MODE, /* The big X button */ + -1 +}; + static const signed short xpad_abs[] = { ABS_X, ABS_Y, /* left stick */ ABS_RX, ABS_RY, /* right stick */ @@ -159,8 +172,12 @@ static const signed short xpad_abs_pad[] = { -1 /* terminating entry */ }; +/* Xbox 360 has a vendor-specific (sub)class, so we cannot match it with only + * USB_INTERFACE_INFO, more to that this device has 4 InterfaceProtocols, + * but we need only one of them. */ static struct usb_device_id xpad_table [] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ + { USB_DEVICE_INTERFACE_PROTOCOL(0x045e, 0x028e, 1) }, /* X-Box 360 controller */ { } }; @@ -174,9 +191,16 @@ struct usb_xpad { unsigned char *idata; /* input data */ dma_addr_t idata_dma; +#ifdef CONFIG_JOYSTICK_XPAD_FF + struct urb *irq_out; /* urb for interrupt out report */ + unsigned char *odata; /* output data */ + dma_addr_t odata_dma; +#endif + char phys[65]; /* physical device path */ int dpad_mapping; /* map d-pad to buttons or to axes */ + int xtype; /* type of xbox device */ }; /* @@ -212,8 +236,8 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d } else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ { input_report_key(dev, BTN_LEFT, data[2] & 0x04); input_report_key(dev, BTN_RIGHT, data[2] & 0x08); - input_report_key(dev, BTN_0, data[2] & 0x01); // up - input_report_key(dev, BTN_1, data[2] & 0x02); // down + input_report_key(dev, BTN_0, data[2] & 0x01); /* up */ + input_report_key(dev, BTN_1, data[2] & 0x02); /* down */ } /* start/back buttons and stick press left/right */ @@ -235,6 +259,64 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d input_sync(dev); } +/* + * xpad360_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. It is version for xbox 360 controller + * + * The used report descriptor was taken from: + * http://www.free60.org/wiki/Gamepad + */ + +static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) +{ + struct input_dev *dev = xpad->dev; + + /* digital pad */ + if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) { + input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); + input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); + } else if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) { + /* dpad as buttons (right, left, down, up) */ + input_report_key(dev, BTN_LEFT, data[2] & 0x04); + input_report_key(dev, BTN_RIGHT, data[2] & 0x08); + input_report_key(dev, BTN_0, data[2] & 0x01); /* up */ + input_report_key(dev, BTN_1, data[2] & 0x02); /* down */ + } + + /* start/back buttons */ + input_report_key(dev, BTN_START, data[2] & 0x10); + input_report_key(dev, BTN_BACK, data[2] & 0x20); + + /* stick press left/right */ + input_report_key(dev, BTN_THUMBL, data[2] & 0x40); + input_report_key(dev, BTN_THUMBR, data[2] & 0x80); + + /* buttons A,B,X,Y,TL,TR and MODE */ + input_report_key(dev, BTN_A, data[3] & 0x10); + input_report_key(dev, BTN_B, data[3] & 0x20); + input_report_key(dev, BTN_X, data[3] & 0x40); + input_report_key(dev, BTN_Y, data[3] & 0x80); + input_report_key(dev, BTN_TL, data[3] & 0x01); + input_report_key(dev, BTN_TR, data[3] & 0x02); + input_report_key(dev, BTN_MODE, data[3] & 0x04); + + /* left stick */ + input_report_abs(dev, ABS_X, (__s16) (((__s16)data[7] << 8) | (__s16)data[6])); + input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[9] << 8) | (__s16)data[8])); + + /* right stick */ + input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[11] << 8) | (__s16)data[10])); + input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[13] << 8) | (__s16)data[12])); + + /* triggers left/right */ + input_report_abs(dev, ABS_Z, data[4]); + input_report_abs(dev, ABS_RZ, data[5]); + + input_sync(dev); +} + static void xpad_irq_in(struct urb *urb) { struct usb_xpad *xpad = urb->context; @@ -255,7 +337,10 @@ static void xpad_irq_in(struct urb *urb) goto exit; } - xpad_process_packet(xpad, 0, xpad->idata); + if (xpad->xtype == XTYPE_XBOX360) + xpad360_process_packet(xpad, 0, xpad->idata); + else + xpad_process_packet(xpad, 0, xpad->idata); exit: retval = usb_submit_urb (urb, GFP_ATOMIC); @@ -264,7 +349,114 @@ exit: __FUNCTION__, retval); } -static int xpad_open (struct input_dev *dev) +#ifdef CONFIG_JOYSTICK_XPAD_FF +static void xpad_irq_out(struct urb *urb) +{ + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + +static int xpad_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + if (effect->type == FF_RUMBLE) { + __u16 strong = effect->u.rumble.strong_magnitude; + __u16 weak = effect->u.rumble.weak_magnitude; + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x08; + xpad->odata[2] = 0x00; + xpad->odata[3] = strong / 256; + xpad->odata[4] = weak / 256; + xpad->odata[5] = 0x00; + xpad->odata[6] = 0x00; + xpad->odata[7] = 0x00; + usb_submit_urb(xpad->irq_out, GFP_KERNEL); + } + + return 0; +} + +static int xpad_init_ff(struct usb_interface *intf, struct usb_xpad *xpad) +{ + struct usb_endpoint_descriptor *ep_irq_out; + int error = -ENOMEM; + + if (xpad->xtype != XTYPE_XBOX360) + return 0; + + xpad->odata = usb_buffer_alloc(xpad->udev, XPAD_PKT_LEN, + GFP_ATOMIC, &xpad->odata_dma ); + if (!xpad->odata) + goto fail1; + + xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->irq_out) + goto fail2; + + ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; + usb_fill_int_urb(xpad->irq_out, xpad->udev, + usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), + xpad->odata, XPAD_PKT_LEN, + xpad_irq_out, xpad, ep_irq_out->bInterval); + xpad->irq_out->transfer_dma = xpad->odata_dma; + xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); + if (error) + goto fail2; + + return 0; + + fail2: usb_buffer_free(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); + fail1: return error; +} + +static void xpad_stop_ff(struct usb_xpad *xpad) +{ + if (xpad->xtype == XTYPE_XBOX360) + usb_kill_urb(xpad->irq_out); +} + +static void xpad_deinit_ff(struct usb_xpad *xpad) +{ + if (xpad->xtype == XTYPE_XBOX360) { + usb_free_urb(xpad->irq_out); + usb_buffer_free(xpad->udev, XPAD_PKT_LEN, + xpad->odata, xpad->odata_dma); + } +} + +#else +static int xpad_init_ff(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } +static void xpad_stop_ff(struct usb_xpad *xpad) { } +static void xpad_deinit_ff(struct usb_xpad *xpad) { } +#endif + +static int xpad_open(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); @@ -275,11 +467,12 @@ static int xpad_open (struct input_dev *dev) return 0; } -static void xpad_close (struct input_dev *dev) +static void xpad_close(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); usb_kill_urb(xpad->irq_in); + xpad_stop_ff(xpad); } static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) @@ -335,6 +528,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->udev = udev; xpad->dpad_mapping = xpad_device[i].dpad_mapping; + xpad->xtype = xpad_device[i].xtype; if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) xpad->dpad_mapping = dpad_to_buttons; xpad->dev = input_dev; @@ -356,6 +550,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id /* set up buttons */ for (i = 0; xpad_btn[i] >= 0; i++) set_bit(xpad_btn[i], input_dev->keybit); + if (xpad->xtype == XTYPE_XBOX360) + for (i = 0; xpad360_btn[i] >= 0; i++) + set_bit(xpad360_btn[i], input_dev->keybit); if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) for (i = 0; xpad_btn_pad[i] >= 0; i++) set_bit(xpad_btn_pad[i], input_dev->keybit); @@ -367,6 +564,10 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id for (i = 0; xpad_abs_pad[i] >= 0; i++) xpad_set_up_abs(input_dev, xpad_abs_pad[i]); + error = xpad_init_ff(intf, xpad); + if (error) + goto fail2; + ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; usb_fill_int_urb(xpad->irq_in, udev, usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), @@ -396,10 +597,10 @@ static void xpad_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (xpad) { - usb_kill_urb(xpad->irq_in); input_unregister_device(xpad->dev); + xpad_deinit_ff(xpad); usb_free_urb(xpad->irq_in); - usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, + usb_buffer_free(xpad->udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); kfree(xpad); } diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 9950fcb3365..41fc3d03b6e 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -89,7 +89,7 @@ static unsigned char atkbd_set2_keycode[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, - 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 159, 0,115, 0,164, 0, 0,116,158, 0,172,166, 0, 0, 0,142, 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, @@ -111,7 +111,7 @@ static unsigned char atkbd_set3_keycode[512] = { 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55,183, 184,185,186,187, 74, 94, 92, 93, 0, 0, 0,125,126,127,112, 0, - 0,139,150,163,165,115,152,150,166,140,160,154,113,114,167,168, + 0,139,172,163,165,115,152,172,166,140,160,154,113,114,167,168, 148,149,147,140 }; diff --git a/drivers/input/keyboard/pxa27x_keyboard.c b/drivers/input/keyboard/pxa27x_keyboard.c index f9e82c9ca42..ebe5eacf299 100644 --- a/drivers/input/keyboard/pxa27x_keyboard.c +++ b/drivers/input/keyboard/pxa27x_keyboard.c @@ -140,7 +140,7 @@ static int pxakbd_resume(struct platform_device *pdev) KPREC = pdata->reg_kprec; /* Enable unit clock */ - pxa_set_cken(CKEN19_KEYPAD, 1); + pxa_set_cken(CKEN_KEYPAD, 1); } mutex_unlock(&input_dev->mutex); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 88e29074ac9..9b26574f146 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -65,9 +65,13 @@ config INPUT_COBALT_BTNS config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86 && !X86_64 + select INPUT_POLLDEV + select NEW_LEDS + select LEDS_CLASS help Say Y here for support of Winstron laptop button interface, used on - laptops of various brands, including Acer and Fujitsu-Siemens. + laptops of various brands, including Acer and Fujitsu-Siemens. If + available, mail and wifi leds will be controlable via /sys/class/leds. To compile this driver as a module, choose M here: the module will be called wistron_btns. diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 961aad7a047..60121f10f8d 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -20,37 +20,31 @@ #include <linux/io.h> #include <linux/dmi.h> #include <linux/init.h> -#include <linux/input.h> +#include <linux/input-polldev.h> #include <linux/interrupt.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/mc146818rtc.h> #include <linux/module.h> #include <linux/preempt.h> #include <linux/string.h> -#include <linux/timer.h> #include <linux/types.h> #include <linux/platform_device.h> +#include <linux/leds.h> -/* - * Number of attempts to read data from queue per poll; - * the queue can hold up to 31 entries - */ -#define MAX_POLL_ITERATIONS 64 - -#define POLL_FREQUENCY 10 /* Number of polls per second */ - -#if POLL_FREQUENCY > HZ -#error "POLL_FREQUENCY too high" -#endif +/* How often we poll keys - msecs */ +#define POLL_INTERVAL_DEFAULT 500 /* when idle */ +#define POLL_INTERVAL_BURST 100 /* when a key was recently pressed */ /* BIOS subsystem IDs */ #define WIFI 0x35 #define BLUETOOTH 0x34 +#define MAIL_LED 0x31 MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>"); MODULE_DESCRIPTION("Wistron laptop button driver"); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.2"); +MODULE_VERSION("0.3"); static int force; /* = 0; */ module_param(force, bool, 0); @@ -248,9 +242,10 @@ enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH }; #define FE_WIFI_LED 0x02 #define FE_UNTESTED 0x80 -static const struct key_entry *keymap; /* = NULL; Current key map */ +static struct key_entry *keymap; /* = NULL; Current key map */ static int have_wifi; static int have_bluetooth; +static int have_leds; static int __init dmi_matched(struct dmi_system_id *dmi) { @@ -263,6 +258,8 @@ static int __init dmi_matched(struct dmi_system_id *dmi) else if (key->type == KE_BLUETOOTH) have_bluetooth = 1; } + have_leds = key->code & (FE_MAIL_LED | FE_WIFI_LED); + return 1; } @@ -966,118 +963,163 @@ static int __init select_keymap(void) /* Input layer interface */ -static struct input_dev *input_dev; +static struct input_polled_dev *wistron_idev; +static unsigned long jiffies_last_press; +static int wifi_enabled; +static int bluetooth_enabled; -static int __devinit setup_input_dev(void) +static void report_key(struct input_dev *dev, unsigned int keycode) { - const struct key_entry *key; - int error; + input_report_key(dev, keycode, 1); + input_sync(dev); + input_report_key(dev, keycode, 0); + input_sync(dev); +} - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; +static void report_switch(struct input_dev *dev, unsigned int code, int value) +{ + input_report_switch(dev, code, value); + input_sync(dev); +} - input_dev->name = "Wistron laptop buttons"; - input_dev->phys = "wistron/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->cdev.dev = &wistron_device->dev; - for (key = keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, input_dev->evbit); - set_bit(key->keycode, input_dev->keybit); - break; + /* led management */ +static void wistron_mail_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + bios_set_state(MAIL_LED, (value != LED_OFF) ? 1 : 0); +} - case KE_SW: - set_bit(EV_SW, input_dev->evbit); - set_bit(key->sw.code, input_dev->swbit); - break; +/* same as setting up wifi card, but for laptops on which the led is managed */ +static void wistron_wifi_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + bios_set_state(WIFI, (value != LED_OFF) ? 1 : 0); +} - default: - ; - } - } +static struct led_classdev wistron_mail_led = { + .name = "mail:green", + .brightness_set = wistron_mail_led_set, +}; - /* reads information flags on KE_END */ - if (key->code & FE_UNTESTED) - printk(KERN_WARNING "Untested laptop multimedia keys, " - "please report success or failure to eric.piel" - "@tremplin-utc.net\n"); +static struct led_classdev wistron_wifi_led = { + .name = "wifi:red", + .brightness_set = wistron_wifi_led_set, +}; - error = input_register_device(input_dev); - if (error) { - input_free_device(input_dev); - return error; +static void __devinit wistron_led_init(struct device *parent) +{ + if (have_leds & FE_WIFI_LED) { + u16 wifi = bios_get_default_setting(WIFI); + if (wifi & 1) { + wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF; + if (led_classdev_register(parent, &wistron_wifi_led)) + have_leds &= ~FE_WIFI_LED; + else + bios_set_state(WIFI, wistron_wifi_led.brightness); + + } else + have_leds &= ~FE_WIFI_LED; } - return 0; + if (have_leds & FE_MAIL_LED) { + /* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */ + wistron_mail_led.brightness = LED_OFF; + if (led_classdev_register(parent, &wistron_mail_led)) + have_leds &= ~FE_MAIL_LED; + else + bios_set_state(MAIL_LED, wistron_mail_led.brightness); + } } -static void report_key(unsigned keycode) +static void __devexit wistron_led_remove(void) { - input_report_key(input_dev, keycode, 1); - input_sync(input_dev); - input_report_key(input_dev, keycode, 0); - input_sync(input_dev); + if (have_leds & FE_MAIL_LED) + led_classdev_unregister(&wistron_mail_led); + + if (have_leds & FE_WIFI_LED) + led_classdev_unregister(&wistron_wifi_led); } -static void report_switch(unsigned code, int value) +static inline void wistron_led_suspend(void) { - input_report_switch(input_dev, code, value); - input_sync(input_dev); + if (have_leds & FE_MAIL_LED) + led_classdev_suspend(&wistron_mail_led); + + if (have_leds & FE_WIFI_LED) + led_classdev_suspend(&wistron_wifi_led); } - /* Driver core */ +static inline void wistron_led_resume(void) +{ + if (have_leds & FE_MAIL_LED) + led_classdev_resume(&wistron_mail_led); -static int wifi_enabled; -static int bluetooth_enabled; + if (have_leds & FE_WIFI_LED) + led_classdev_resume(&wistron_wifi_led); +} + +static struct key_entry *wistron_get_entry_by_scancode(int code) +{ + struct key_entry *key; -static void poll_bios(unsigned long); + for (key = keymap; key->type != KE_END; key++) + if (code == key->code) + return key; -static struct timer_list poll_timer = TIMER_INITIALIZER(poll_bios, 0, 0); + return NULL; +} -static void handle_key(u8 code) +static struct key_entry *wistron_get_entry_by_keycode(int keycode) { - const struct key_entry *key; + struct key_entry *key; - for (key = keymap; key->type != KE_END; key++) { - if (code == key->code) { - switch (key->type) { - case KE_KEY: - report_key(key->keycode); - break; + for (key = keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; - case KE_SW: - report_switch(key->sw.code, key->sw.value); - break; + return NULL; +} - case KE_WIFI: - if (have_wifi) { - wifi_enabled = !wifi_enabled; - bios_set_state(WIFI, wifi_enabled); - } - break; +static void handle_key(u8 code) +{ + const struct key_entry *key = wistron_get_entry_by_scancode(code); - case KE_BLUETOOTH: - if (have_bluetooth) { - bluetooth_enabled = !bluetooth_enabled; - bios_set_state(BLUETOOTH, bluetooth_enabled); - } - break; + if (key) { + switch (key->type) { + case KE_KEY: + report_key(wistron_idev->input, key->keycode); + break; - case KE_END: - break; - default: - BUG(); + case KE_SW: + report_switch(wistron_idev->input, + key->sw.code, key->sw.value); + break; + + case KE_WIFI: + if (have_wifi) { + wifi_enabled = !wifi_enabled; + bios_set_state(WIFI, wifi_enabled); + } + break; + + case KE_BLUETOOTH: + if (have_bluetooth) { + bluetooth_enabled = !bluetooth_enabled; + bios_set_state(BLUETOOTH, bluetooth_enabled); } - return; + break; + + default: + BUG(); } - } - printk(KERN_NOTICE "wistron_btns: Unknown key code %02X\n", code); + jiffies_last_press = jiffies; + } else + printk(KERN_NOTICE + "wistron_btns: Unknown key code %02X\n", code); } -static void poll_bios(unsigned long discard) +static void poll_bios(bool discard) { u8 qlen; u16 val; @@ -1090,15 +1132,118 @@ static void poll_bios(unsigned long discard) if (val != 0 && !discard) handle_key((u8)val); } +} + +static void wistron_flush(struct input_polled_dev *dev) +{ + /* Flush stale event queue */ + poll_bios(true); +} + +static void wistron_poll(struct input_polled_dev *dev) +{ + poll_bios(false); + + /* Increase poll frequency if user is currently pressing keys (< 2s ago) */ + if (time_before(jiffies, jiffies_last_press + 2 * HZ)) + dev->poll_interval = POLL_INTERVAL_BURST; + else + dev->poll_interval = POLL_INTERVAL_DEFAULT; +} + +static int wistron_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + const struct key_entry *key = wistron_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int wistron_setkeycode(struct input_dev *dev, int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = wistron_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!wistron_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } - mod_timer(&poll_timer, jiffies + HZ / POLL_FREQUENCY); + return -EINVAL; } +static int __devinit setup_input_dev(void) +{ + const struct key_entry *key; + struct input_dev *input_dev; + int error; + + wistron_idev = input_allocate_polled_device(); + if (!wistron_idev) + return -ENOMEM; + + wistron_idev->flush = wistron_flush; + wistron_idev->poll = wistron_poll; + wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT; + + input_dev = wistron_idev->input; + input_dev->name = "Wistron laptop buttons"; + input_dev->phys = "wistron/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &wistron_device->dev; + + input_dev->getkeycode = wistron_getkeycode; + input_dev->setkeycode = wistron_setkeycode; + + for (key = keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, input_dev->evbit); + set_bit(key->keycode, input_dev->keybit); + break; + + case KE_SW: + set_bit(EV_SW, input_dev->evbit); + set_bit(key->sw.code, input_dev->swbit); + break; + + default: + break; + } + } + + /* reads information flags on KE_END */ + if (key->code & FE_UNTESTED) + printk(KERN_WARNING "Untested laptop multimedia keys, " + "please report success or failure to eric.piel" + "@tremplin-utc.net\n"); + + error = input_register_polled_device(wistron_idev); + if (error) { + input_free_polled_device(wistron_idev); + return error; + } + + return 0; +} + +/* Driver core */ + static int __devinit wistron_probe(struct platform_device *dev) { - int err = setup_input_dev(); - if (err) - return err; + int err; bios_attach(); cmos_address = bios_get_cmos_address(); @@ -1125,15 +1270,21 @@ static int __devinit wistron_probe(struct platform_device *dev) bios_set_state(BLUETOOTH, bluetooth_enabled); } - poll_bios(1); /* Flush stale event queue and arm timer */ + wistron_led_init(&dev->dev); + err = setup_input_dev(); + if (err) { + bios_detach(); + return err; + } return 0; } static int __devexit wistron_remove(struct platform_device *dev) { - del_timer_sync(&poll_timer); - input_unregister_device(input_dev); + wistron_led_remove(); + input_unregister_polled_device(wistron_idev); + input_free_polled_device(wistron_idev); bios_detach(); return 0; @@ -1142,14 +1293,13 @@ static int __devexit wistron_remove(struct platform_device *dev) #ifdef CONFIG_PM static int wistron_suspend(struct platform_device *dev, pm_message_t state) { - del_timer_sync(&poll_timer); - if (have_wifi) bios_set_state(WIFI, 0); if (have_bluetooth) bios_set_state(BLUETOOTH, 0); + wistron_led_suspend(); return 0; } @@ -1161,7 +1311,8 @@ static int wistron_resume(struct platform_device *dev) if (have_bluetooth) bios_set_state(BLUETOOTH, bluetooth_enabled); - poll_bios(1); + wistron_led_resume(); + poll_bios(true); return 0; } diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 50e06e8dd05..7bbea097cda 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -216,4 +216,20 @@ config MOUSE_HIL help Say Y here to support HIL pointers. +config MOUSE_GPIO + tristate "GPIO mouse" + depends on GENERIC_GPIO + select INPUT_POLLDEV + help + This driver simulates a mouse on GPIO lines of various CPUs (and some + other chips). + + Say Y here if your device has buttons or a simple joystick connected + directly to GPIO lines. Your board-specific setup logic must also + provide a platform device and platform data saying which GPIOs are + used. + + To compile this driver as a module, choose M here: the + module will be called gpio_mouse. + endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index aa4ba878533..9e6e3633082 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o +obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o psmouse-objs := psmouse-base.o synaptics.o diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c new file mode 100644 index 00000000000..0936d6ba015 --- /dev/null +++ b/drivers/input/mouse/gpio_mouse.c @@ -0,0 +1,196 @@ +/* + * Driver for simulating a mouse on GPIO lines. + * + * Copyright (C) 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/input-polldev.h> +#include <linux/gpio_mouse.h> + +#include <asm/gpio.h> + +/* + * Timer function which is run every scan_ms ms when the device is opened. + * The dev input varaible is set to the the input_dev pointer. + */ +static void gpio_mouse_scan(struct input_polled_dev *dev) +{ + struct gpio_mouse_platform_data *gpio = dev->private; + struct input_dev *input = dev->input; + int x, y; + + if (gpio->bleft >= 0) + input_report_key(input, BTN_LEFT, + gpio_get_value(gpio->bleft) ^ gpio->polarity); + if (gpio->bmiddle >= 0) + input_report_key(input, BTN_MIDDLE, + gpio_get_value(gpio->bmiddle) ^ gpio->polarity); + if (gpio->bright >= 0) + input_report_key(input, BTN_RIGHT, + gpio_get_value(gpio->bright) ^ gpio->polarity); + + x = (gpio_get_value(gpio->right) ^ gpio->polarity) + - (gpio_get_value(gpio->left) ^ gpio->polarity); + y = (gpio_get_value(gpio->down) ^ gpio->polarity) + - (gpio_get_value(gpio->up) ^ gpio->polarity); + + input_report_rel(input, REL_X, x); + input_report_rel(input, REL_Y, y); + input_sync(input); +} + +static int __init gpio_mouse_probe(struct platform_device *pdev) +{ + struct gpio_mouse_platform_data *pdata = pdev->dev.platform_data; + struct input_polled_dev *input_poll; + struct input_dev *input; + int pin, i; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + error = -ENXIO; + goto out; + } + + if (pdata->scan_ms < 0) { + dev_err(&pdev->dev, "invalid scan time\n"); + error = -EINVAL; + goto out; + } + + for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) { + pin = pdata->pins[i]; + + if (pin < 0) { + + if (i <= GPIO_MOUSE_PIN_RIGHT) { + /* Mouse direction is required. */ + dev_err(&pdev->dev, + "missing GPIO for directions\n"); + error = -EINVAL; + goto out_free_gpios; + } + + if (i == GPIO_MOUSE_PIN_BLEFT) + dev_dbg(&pdev->dev, "no left button defined\n"); + + } else { + error = gpio_request(pin, "gpio_mouse"); + if (error) { + dev_err(&pdev->dev, "fail %d pin (%d idx)\n", + pin, i); + goto out_free_gpios; + } + + gpio_direction_input(pin); + } + } + + input_poll = input_allocate_polled_device(); + if (!input_poll) { + dev_err(&pdev->dev, "not enough memory for input device\n"); + error = -ENOMEM; + goto out_free_gpios; + } + + platform_set_drvdata(pdev, input_poll); + + /* set input-polldev handlers */ + input_poll->private = pdata; + input_poll->poll = gpio_mouse_scan; + input_poll->poll_interval = pdata->scan_ms; + + input = input_poll->input; + input->name = pdev->name; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_REL, REL_X); + input_set_capability(input, EV_REL, REL_Y); + if (pdata->bleft >= 0) + input_set_capability(input, EV_KEY, BTN_LEFT); + if (pdata->bmiddle >= 0) + input_set_capability(input, EV_KEY, BTN_MIDDLE); + if (pdata->bright >= 0) + input_set_capability(input, EV_KEY, BTN_RIGHT); + + error = input_register_polled_device(input_poll); + if (error) { + dev_err(&pdev->dev, "could not register input device\n"); + goto out_free_polldev; + } + + dev_dbg(&pdev->dev, "%d ms scan time, buttons: %s%s%s\n", + pdata->scan_ms, + pdata->bleft < 0 ? "" : "left ", + pdata->bmiddle < 0 ? "" : "middle ", + pdata->bright < 0 ? "" : "right"); + + return 0; + + out_free_polldev: + input_free_polled_device(input_poll); + platform_set_drvdata(pdev, NULL); + + out_free_gpios: + while (--i >= 0) { + pin = pdata->pins[i]; + if (pin) + gpio_free(pin); + } + out: + return error; +} + +static int __devexit gpio_mouse_remove(struct platform_device *pdev) +{ + struct input_polled_dev *input = platform_get_drvdata(pdev); + struct gpio_mouse_platform_data *pdata = input->private; + int pin, i; + + input_unregister_polled_device(input); + input_free_polled_device(input); + + for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) { + pin = pdata->pins[i]; + if (pin >= 0) + gpio_free(pin); + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +struct platform_driver gpio_mouse_device_driver = { + .remove = __devexit_p(gpio_mouse_remove), + .driver = { + .name = "gpio_mouse", + } +}; + +static int __init gpio_mouse_init(void) +{ + return platform_driver_probe(&gpio_mouse_device_driver, + gpio_mouse_probe); +} +module_init(gpio_mouse_init); + +static void __exit gpio_mouse_exit(void) +{ + platform_driver_unregister(&gpio_mouse_device_driver); +} +module_exit(gpio_mouse_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); +MODULE_DESCRIPTION("GPIO mouse driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index f15f695777f..b9f0fb2530e 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -178,6 +178,15 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) } /* + * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first + * byte. + */ + if (psmouse->type == PSMOUSE_CORTRON) { + input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); + packet[0] |= 0x08; + } + +/* * Generic PS/2 Mouse */ @@ -539,6 +548,20 @@ static int ps2bare_detect(struct psmouse *psmouse, int set_properties) return 0; } +/* + * Cortron PS/2 protocol detection. There's no special way to detect it, so it + * must be forced by sysfs protocol writing. + */ +static int cortron_detect(struct psmouse *psmouse, int set_properties) +{ + if (set_properties) { + psmouse->vendor = "Cortron"; + psmouse->name = "PS/2 Trackball"; + set_bit(BTN_SIDE, psmouse->dev->keybit); + } + + return 0; +} /* * psmouse_extensions() probes for any extensions to the basic PS/2 protocol @@ -740,6 +763,12 @@ static const struct psmouse_protocol psmouse_protocols[] = { }, #endif { + .type = PSMOUSE_CORTRON, + .name = "CortronPS/2", + .alias = "cortps", + .detect = cortron_detect, + }, + { .type = PSMOUSE_AUTO, .name = "auto", .alias = "any", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 3964e8acbc5..27a68835b5b 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -88,6 +88,7 @@ enum psmouse_type { PSMOUSE_LIFEBOOK, PSMOUSE_TRACKPOINT, PSMOUSE_TOUCHKIT_PS2, + PSMOUSE_CORTRON, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 3f4866d8d18..9173916b8be 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -64,6 +64,7 @@ struct mousedev { wait_queue_head_t wait; struct list_head client_list; struct input_handle handle; + struct device dev; struct list_head mixdev_node; int mixdev_open; @@ -112,7 +113,7 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; static struct input_handler mousedev_handler; static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; -static struct mousedev mousedev_mix; +static struct mousedev *mousedev_mix; static LIST_HEAD(mousedev_mix_list); #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) @@ -218,10 +219,10 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int if (value) { set_bit(index, &mousedev->packet.buttons); - set_bit(index, &mousedev_mix.packet.buttons); + set_bit(index, &mousedev_mix->packet.buttons); } else { clear_bit(index, &mousedev->packet.buttons); - clear_bit(index, &mousedev_mix.packet.buttons); + clear_bit(index, &mousedev_mix->packet.buttons); } } @@ -287,11 +288,11 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) * motion packet so we won't mess current position. */ set_bit(0, &mousedev->packet.buttons); - set_bit(0, &mousedev_mix.packet.buttons); - mousedev_notify_readers(mousedev, &mousedev_mix.packet); - mousedev_notify_readers(&mousedev_mix, &mousedev_mix.packet); + set_bit(0, &mousedev_mix->packet.buttons); + mousedev_notify_readers(mousedev, &mousedev_mix->packet); + mousedev_notify_readers(mousedev_mix, &mousedev_mix->packet); clear_bit(0, &mousedev->packet.buttons); - clear_bit(0, &mousedev_mix.packet.buttons); + clear_bit(0, &mousedev_mix->packet.buttons); } mousedev->touch = mousedev->pkt_count = 0; mousedev->frac_dx = 0; @@ -343,7 +344,7 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig } mousedev_notify_readers(mousedev, &mousedev->packet); - mousedev_notify_readers(&mousedev_mix, &mousedev->packet); + mousedev_notify_readers(mousedev_mix, &mousedev->packet); mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0; mousedev->packet.abs_event = 0; @@ -362,8 +363,10 @@ static int mousedev_fasync(int fd, struct file *file, int on) return retval < 0 ? retval : 0; } -static void mousedev_free(struct mousedev *mousedev) +static void mousedev_free(struct device *dev) { + struct mousedev *mousedev = container_of(dev, struct mousedev, dev); + mousedev_table[mousedev->minor] = NULL; kfree(mousedev); } @@ -372,15 +375,16 @@ static int mixdev_add_device(struct mousedev *mousedev) { int error; - if (mousedev_mix.open) { + if (mousedev_mix->open) { error = input_open_device(&mousedev->handle); if (error) return error; mousedev->open++; - mousedev->mixdev_open++; + mousedev->mixdev_open = 1; } + get_device(&mousedev->dev); list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); return 0; @@ -395,36 +399,40 @@ static void mixdev_remove_device(struct mousedev *mousedev) } list_del_init(&mousedev->mixdev_node); + put_device(&mousedev->dev); } static void mixdev_open_devices(void) { struct mousedev *mousedev; + if (mousedev_mix->open++) + return; + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { - if (mousedev->exist && !mousedev->open) { - if (input_open_device(&mousedev->handle)) - continue; + if (!mousedev->mixdev_open) { + if (!mousedev->open && mousedev->exist) + if (input_open_device(&mousedev->handle)) + continue; mousedev->open++; - mousedev->mixdev_open++; + mousedev->mixdev_open = 1; } } } static void mixdev_close_devices(void) { - struct mousedev *mousedev, *next; + struct mousedev *mousedev; - list_for_each_entry_safe(mousedev, next, &mousedev_mix_list, mixdev_node) { + if (--mousedev_mix->open) + return; + + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { if (mousedev->mixdev_open) { mousedev->mixdev_open = 0; - if (!--mousedev->open) { - if (mousedev->exist) - input_close_device(&mousedev->handle); - else - mousedev_free(mousedev); - } + if (!--mousedev->open && mousedev->exist) + input_close_device(&mousedev->handle); } } } @@ -439,14 +447,12 @@ static int mousedev_release(struct inode *inode, struct file *file) list_del(&client->node); kfree(client); - if (!--mousedev->open) { - if (mousedev->minor == MOUSEDEV_MIX) - mixdev_close_devices(); - else if (mousedev->exist) - input_close_device(&mousedev->handle); - else - mousedev_free(mousedev); - } + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_close_devices(); + else if (!--mousedev->open && mousedev->exist) + input_close_device(&mousedev->handle); + + put_device(&mousedev->dev); return 0; } @@ -473,9 +479,13 @@ static int mousedev_open(struct inode *inode, struct file *file) if (!mousedev) return -ENODEV; + get_device(&mousedev->dev); + client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); - if (!client) - return -ENOMEM; + if (!client) { + error = -ENOMEM; + goto err_put_mousedev; + } spin_lock_init(&client->packet_lock); client->pos_x = xres / 2; @@ -483,21 +493,23 @@ static int mousedev_open(struct inode *inode, struct file *file) client->mousedev = mousedev; list_add_tail(&client->node, &mousedev->client_list); - if (!mousedev->open++) { - if (mousedev->minor == MOUSEDEV_MIX) - mixdev_open_devices(); - else if (mousedev->exist) { - error = input_open_device(&mousedev->handle); - if (error) { - list_del(&client->node); - kfree(client); - return error; - } - } + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_open_devices(); + else if (!mousedev->open++ && mousedev->exist) { + error = input_open_device(&mousedev->handle); + if (error) + goto err_free_client; } file->private_data = client; return 0; + + err_free_client: + list_del(&client->node); + kfree(client); + err_put_mousedev: + put_device(&mousedev->dev); + return error; } static inline int mousedev_limit_delta(int delta, int limit) @@ -680,57 +692,96 @@ static const struct file_operations mousedev_fops = { .fasync = mousedev_fasync, }; -static int mousedev_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static struct mousedev *mousedev_create(struct input_dev *dev, + struct input_handler *handler, + int minor) { struct mousedev *mousedev; - struct class_device *cdev; - dev_t devt; - int minor; int error; - for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); - if (minor == MOUSEDEV_MINORS) { - printk(KERN_ERR "mousedev: no more free mousedev devices\n"); - return -ENFILE; - } - mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); - if (!mousedev) - return -ENOMEM; + if (!mousedev) { + error = -ENOMEM; + goto err_out; + } INIT_LIST_HEAD(&mousedev->client_list); INIT_LIST_HEAD(&mousedev->mixdev_node); init_waitqueue_head(&mousedev->wait); + if (minor == MOUSEDEV_MIX) + strlcpy(mousedev->name, "mice", sizeof(mousedev->name)); + else + snprintf(mousedev->name, sizeof(mousedev->name), + "mouse%d", minor); + mousedev->minor = minor; mousedev->exist = 1; mousedev->handle.dev = dev; mousedev->handle.name = mousedev->name; mousedev->handle.handler = handler; mousedev->handle.private = mousedev; - sprintf(mousedev->name, "mouse%d", minor); - mousedev_table[minor] = mousedev; + strlcpy(mousedev->dev.bus_id, mousedev->name, + sizeof(mousedev->dev.bus_id)); + mousedev->dev.class = &input_class; + if (dev) + mousedev->dev.parent = &dev->dev; + mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor); + mousedev->dev.release = mousedev_free; + device_initialize(&mousedev->dev); - devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor), + mousedev_table[minor] = mousedev; - cdev = class_device_create(&input_class, &dev->cdev, devt, - dev->cdev.dev, mousedev->name); - if (IS_ERR(cdev)) { - error = PTR_ERR(cdev); + error = device_add(&mousedev->dev); + if (error) goto err_free_mousedev; + + return mousedev; + + err_free_mousedev: + put_device(&mousedev->dev); + err_out: + return ERR_PTR(error); +} + +static void mousedev_destroy(struct mousedev *mousedev) +{ + struct mousedev_client *client; + + device_del(&mousedev->dev); + mousedev->exist = 0; + + if (mousedev->open) { + input_close_device(&mousedev->handle); + list_for_each_entry(client, &mousedev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); + wake_up_interruptible(&mousedev->wait); } - /* temporary symlink to keep userspace happy */ - error = sysfs_create_link(&input_class.subsys.kobj, - &cdev->kobj, mousedev->name); - if (error) - goto err_cdev_destroy; + put_device(&mousedev->dev); +} + +static int mousedev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct mousedev *mousedev; + int minor; + int error; + + for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); + if (minor == MOUSEDEV_MINORS) { + printk(KERN_ERR "mousedev: no more free mousedev devices\n"); + return -ENFILE; + } + + mousedev = mousedev_create(dev, handler, minor); + if (IS_ERR(mousedev)) + return PTR_ERR(mousedev); error = input_register_handle(&mousedev->handle); if (error) - goto err_remove_link; + goto err_delete_mousedev; error = mixdev_add_device(mousedev); if (error) @@ -740,37 +791,18 @@ static int mousedev_connect(struct input_handler *handler, struct input_dev *dev err_unregister_handle: input_unregister_handle(&mousedev->handle); - err_remove_link: - sysfs_remove_link(&input_class.subsys.kobj, mousedev->name); - err_cdev_destroy: - class_device_destroy(&input_class, devt); - err_free_mousedev: - mousedev_table[minor] = NULL; - kfree(mousedev); + err_delete_mousedev: + device_unregister(&mousedev->dev); return error; } static void mousedev_disconnect(struct input_handle *handle) { struct mousedev *mousedev = handle->private; - struct mousedev_client *client; - - input_unregister_handle(handle); - - sysfs_remove_link(&input_class.subsys.kobj, mousedev->name); - class_device_destroy(&input_class, - MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + mousedev->minor)); - mousedev->exist = 0; mixdev_remove_device(mousedev); - - if (mousedev->open) { - input_close_device(handle); - list_for_each_entry(client, &mousedev->client_list, node) - kill_fasync(&client->fasync, SIGIO, POLL_HUP); - wake_up_interruptible(&mousedev->wait); - } else - mousedev_free(mousedev); + input_unregister_handle(handle); + mousedev_destroy(mousedev); } static const struct input_device_id mousedev_ids[] = { @@ -822,25 +854,16 @@ static int psaux_registered; static int __init mousedev_init(void) { - struct class_device *cdev; int error; + mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX); + if (IS_ERR(mousedev_mix)) + return PTR_ERR(mousedev_mix); + error = input_register_handler(&mousedev_handler); - if (error) + if (error) { + mousedev_destroy(mousedev_mix); return error; - - memset(&mousedev_mix, 0, sizeof(struct mousedev)); - INIT_LIST_HEAD(&mousedev_mix.client_list); - init_waitqueue_head(&mousedev_mix.wait); - mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; - mousedev_mix.exist = 1; - mousedev_mix.minor = MOUSEDEV_MIX; - - cdev = class_device_create(&input_class, NULL, - MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice"); - if (IS_ERR(cdev)) { - input_unregister_handler(&mousedev_handler); - return PTR_ERR(cdev); } #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX @@ -863,9 +886,8 @@ static void __exit mousedev_exit(void) if (psaux_registered) misc_deregister(&psaux_mouse); #endif - class_device_destroy(&input_class, - MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX)); input_unregister_handler(&mousedev_handler); + mousedev_destroy(mousedev_mix); } module_init(mousedev_init); diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index 887357666c6..0403622ae26 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -160,7 +160,7 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t cou { struct serio_raw_list *list = file->private_data; struct serio_raw *serio_raw = list->serio_raw; - char c; + char uninitialized_var(c); ssize_t retval = 0; if (!serio_raw->serio) diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index cc0a498763d..94683f58c9e 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -82,8 +82,8 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.5 (May-15-2004)" -#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio" +#define DRIVER_VERSION "v2.3 (May 2, 2007)" +#define DRIVER_AUTHOR "Bryan W. Headley/Chris Atenasio/Cedric Brun/Rene van Paassen" #define DRIVER_DESC "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)" /* @@ -112,7 +112,7 @@ * (returned as Report 3 - absolute coordinates from the mouse) * * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 - * byte0 0 0 0 0 0 0 1 0 + * byte0 0 0 0 0 0 0 1 1 * byte1 X7 X6 X5 X4 X3 X2 X1 X0 * byte2 X15 X14 X13 X12 X11 X10 X9 X8 * byte3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 @@ -134,7 +134,7 @@ * (returned as Report 5 - macrokeys from the mouse) * * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 - * byte0 0 0 0 0 0 1 0 0 + * byte0 0 0 0 0 0 1 0 1 * byte1 0 0 0 BS2 BS Tip IR DV * byte2 0 0 0 0 0 0 1 0 * byte3 0 0 0 K4 K3 K2 K1 K0 @@ -218,15 +218,9 @@ #define AIPTEK_WHEEL_DISABLE (-10101) /* ToolCode values, which BTW are 0x140 .. 0x14f - * We have things set up such that if TOOL_BUTTON_FIRED_BIT is - * not set, we'll send one instance of AIPTEK_TOOL_BUTTON_xxx. - * - * Whenever the user resets the value, TOOL_BUTTON_FIRED_BIT will - * get reset. + * We have things set up such that if the tool button has changed, + * the tools get reset. */ -#define TOOL_BUTTON(x) ((x) & 0x14f) -#define TOOL_BUTTON_FIRED(x) ((x) & 0x200) -#define TOOL_BUTTON_FIRED_BIT 0x200 /* toolMode codes */ #define AIPTEK_TOOL_BUTTON_PEN_MODE BTN_TOOL_PEN @@ -264,9 +258,9 @@ /* Mouse button programming */ -#define AIPTEK_MOUSE_LEFT_BUTTON 0x01 -#define AIPTEK_MOUSE_RIGHT_BUTTON 0x02 -#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x04 +#define AIPTEK_MOUSE_LEFT_BUTTON 0x04 +#define AIPTEK_MOUSE_RIGHT_BUTTON 0x08 +#define AIPTEK_MOUSE_MIDDLE_BUTTON 0x10 /* Stylus button programming */ @@ -294,7 +288,6 @@ struct aiptek_features { int modelCode; /* Tablet model code (not unique) */ int firmwareCode; /* prom/eeprom version */ char usbPath[64 + 1]; /* device's physical usb path */ - char inputPath[64 + 1]; /* input device path */ }; struct aiptek_settings { @@ -327,9 +320,32 @@ struct aiptek { int inDelay; /* jitter: in jitter delay? */ unsigned long endDelay; /* jitter: time when delay ends */ int previousJitterable; /* jitterable prev value */ + + int lastMacro; /* macro key to reset */ + int previousToolMode; /* pen, pencil, brush, etc. tool */ unsigned char *data; /* incoming packet data */ }; +static const int eventTypes[] = { + EV_KEY, EV_ABS, EV_REL, EV_MSC, +}; + +static const int absEvents[] = { + ABS_X, ABS_Y, ABS_PRESSURE, ABS_TILT_X, ABS_TILT_Y, + ABS_WHEEL, ABS_MISC, +}; + +static const int relEvents[] = { + REL_X, REL_Y, REL_WHEEL, +}; + +static const int buttonEvents[] = { + BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, + BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH, + BTN_TOOL_BRUSH, BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOUCH, + BTN_STYLUS, BTN_STYLUS2, +}; + /* * Permit easy lookup of keyboard events to send, versus * the bitmap which comes from the tablet. This hides the @@ -345,23 +361,39 @@ static const int macroKeyEvents[] = { }; /*********************************************************************** - * Relative reports deliver values in 2's complement format to - * deal with negative offsets. + * Map values to strings and back. Every map shoudl have the following + * as its last element: { NULL, AIPTEK_INVALID_VALUE }. */ -static int aiptek_convert_from_2s_complement(unsigned char c) +#define AIPTEK_INVALID_VALUE -1 + +struct aiptek_map { + const char *string; + int value; +}; + +static int map_str_to_val(const struct aiptek_map *map, const char *str, size_t count) { - int ret; - unsigned char b = c; - int negate = 0; + const struct aiptek_map *p; - if ((b & 0x80) != 0) { - b = ~b; - b--; - negate = 1; - } - ret = b; - ret = (negate == 1) ? -ret : ret; - return ret; + if (str[count - 1] == '\n') + count--; + + for (p = map; p->string; p++) + if (!strncmp(str, p->string, count)) + return p->value; + + return AIPTEK_INVALID_VALUE; +} + +static const char *map_val_to_str(const struct aiptek_map *map, int val) +{ + const struct aiptek_map *p; + + for (p = map; p->value != AIPTEK_INVALID_VALUE; p++) + if (val == p->value) + return p->string; + + return "unknown"; } /*********************************************************************** @@ -385,6 +417,9 @@ static int aiptek_convert_from_2s_complement(unsigned char c) * Proximity. Why two events? I thought it interesting to know if the * Proximity event occurred while the tablet was in absolute or relative * mode. + * Update: REL_MISC proved not to be such a good idea. With REL_MISC you + * get an event transmitted each time. ABS_MISC works better, since it + * can be set and re-set. Thus, only using ABS_MISC from now on. * * Other tablets use the notion of a certain minimum stylus pressure * to infer proximity. While that could have been done, that is yet @@ -441,8 +476,8 @@ static void aiptek_irq(struct urb *urb) aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE; } else { - x = aiptek_convert_from_2s_complement(data[2]); - y = aiptek_convert_from_2s_complement(data[3]); + x = (signed char) data[2]; + y = (signed char) data[3]; /* jitterable keeps track of whether any button has been pressed. * We're also using it to remap the physical mouse button mask @@ -451,18 +486,20 @@ static void aiptek_irq(struct urb *urb) * that a non-zero value indicates that one or more * mouse button was pressed.) */ - jitterable = data[5] & 0x07; + jitterable = data[1] & 0x07; - left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; - right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; - middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; + left = (data[1] & aiptek->curSetting.mouseButtonLeft >> 2) != 0 ? 1 : 0; + right = (data[1] & aiptek->curSetting.mouseButtonRight >> 2) != 0 ? 1 : 0; + middle = (data[1] & aiptek->curSetting.mouseButtonMiddle >> 2) != 0 ? 1 : 0; input_report_key(inputdev, BTN_LEFT, left); input_report_key(inputdev, BTN_MIDDLE, middle); input_report_key(inputdev, BTN_RIGHT, right); + + input_report_abs(inputdev, ABS_MISC, + 1 | AIPTEK_REPORT_TOOL_UNKNOWN); input_report_rel(inputdev, REL_X, x); input_report_rel(inputdev, REL_Y, y); - input_report_rel(inputdev, REL_MISC, 1 | AIPTEK_REPORT_TOOL_UNKNOWN); /* Wheel support is in the form of a single-event * firing. @@ -472,6 +509,11 @@ static void aiptek_irq(struct urb *urb) aiptek->curSetting.wheel); aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; } + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } input_sync(inputdev); } } @@ -489,8 +531,8 @@ static void aiptek_irq(struct urb *urb) y = le16_to_cpu(get_unaligned((__le16 *) (data + 3))); z = le16_to_cpu(get_unaligned((__le16 *) (data + 6))); - p = (data[5] & 0x01) != 0 ? 1 : 0; - dv = (data[5] & 0x02) != 0 ? 1 : 0; + dv = (data[5] & 0x01) != 0 ? 1 : 0; + p = (data[5] & 0x02) != 0 ? 1 : 0; tip = (data[5] & 0x04) != 0 ? 1 : 0; /* Use jitterable to re-arrange button masks @@ -505,16 +547,18 @@ static void aiptek_irq(struct urb *urb) * all 'bad' reports... */ if (dv != 0) { - /* If we've not already sent a tool_button_?? code, do - * so now. Then set FIRED_BIT so it won't be resent unless - * the user forces FIRED_BIT off. + /* If the selected tool changed, reset the old + * tool key, and set the new one. */ - if (TOOL_BUTTON_FIRED - (aiptek->curSetting.toolMode) == 0) { + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting.toolMode), + aiptek->curSetting.toolMode, 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + aiptek->previousToolMode = + aiptek->curSetting.toolMode; } if (p != 0) { @@ -550,6 +594,11 @@ static void aiptek_irq(struct urb *urb) } } input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS); + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } input_sync(inputdev); } } @@ -568,23 +617,25 @@ static void aiptek_irq(struct urb *urb) jitterable = data[5] & 0x1c; - p = (data[5] & 0x01) != 0 ? 1 : 0; - dv = (data[5] & 0x02) != 0 ? 1 : 0; + dv = (data[5] & 0x01) != 0 ? 1 : 0; + p = (data[5] & 0x02) != 0 ? 1 : 0; left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; if (dv != 0) { - /* If we've not already sent a tool_button_?? code, do - * so now. Then set FIRED_BIT so it won't be resent unless - * the user forces FIRED_BIT off. + /* If the selected tool changed, reset the old + * tool key, and set the new one. */ - if (TOOL_BUTTON_FIRED - (aiptek->curSetting.toolMode) == 0) { + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting.toolMode), + aiptek->curSetting.toolMode, 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + aiptek->previousToolMode = + aiptek->curSetting.toolMode; } if (p != 0) { @@ -605,7 +656,12 @@ static void aiptek_irq(struct urb *urb) aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE; } } - input_report_rel(inputdev, REL_MISC, p | AIPTEK_REPORT_TOOL_MOUSE); + input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_MOUSE); + if (aiptek->lastMacro != -1) { + input_report_key(inputdev, + macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } input_sync(inputdev); } } @@ -615,98 +671,83 @@ static void aiptek_irq(struct urb *urb) else if (data[0] == 4) { jitterable = data[1] & 0x18; - p = (data[1] & 0x01) != 0 ? 1 : 0; - dv = (data[1] & 0x02) != 0 ? 1 : 0; + dv = (data[1] & 0x01) != 0 ? 1 : 0; + p = (data[1] & 0x02) != 0 ? 1 : 0; tip = (data[1] & 0x04) != 0 ? 1 : 0; bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0; pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0; - macro = data[3]; + macro = dv && p && tip && !(data[3] & 1) ? (data[3] >> 1) : -1; z = le16_to_cpu(get_unaligned((__le16 *) (data + 4))); - if (dv != 0) { - /* If we've not already sent a tool_button_?? code, do - * so now. Then set FIRED_BIT so it won't be resent unless - * the user forces FIRED_BIT off. + if (dv) { + /* If the selected tool changed, reset the old + * tool key, and set the new one. */ - if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) { + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting.toolMode), + aiptek->curSetting.toolMode, 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + aiptek->previousToolMode = + aiptek->curSetting.toolMode; } + } - if (p != 0) { - input_report_key(inputdev, BTN_TOUCH, tip); - input_report_key(inputdev, BTN_STYLUS, bs); - input_report_key(inputdev, BTN_STYLUS2, pck); - input_report_abs(inputdev, ABS_PRESSURE, z); - } + if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { + input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } - /* For safety, we're sending key 'break' codes for the - * neighboring macro keys. - */ - if (macro > 0) { - input_report_key(inputdev, - macroKeyEvents[macro - 1], 0); - } - if (macro < 25) { - input_report_key(inputdev, - macroKeyEvents[macro + 1], 0); - } - input_report_key(inputdev, macroKeyEvents[macro], p); - input_report_abs(inputdev, ABS_MISC, - p | AIPTEK_REPORT_TOOL_STYLUS); - input_sync(inputdev); + if (macro != -1 && macro != aiptek->lastMacro) { + input_report_key(inputdev, macroKeyEvents[macro], 1); + aiptek->lastMacro = macro; } + input_report_abs(inputdev, ABS_MISC, + p | AIPTEK_REPORT_TOOL_STYLUS); + input_sync(inputdev); } /* Report 5s come from the macro keys when pressed by mouse */ else if (data[0] == 5) { jitterable = data[1] & 0x1c; - p = (data[1] & 0x01) != 0 ? 1 : 0; - dv = (data[1] & 0x02) != 0 ? 1 : 0; + dv = (data[1] & 0x01) != 0 ? 1 : 0; + p = (data[1] & 0x02) != 0 ? 1 : 0; left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0; right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0; middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0; - macro = data[3]; + macro = dv && p && left && !(data[3] & 1) ? (data[3] >> 1) : 0; - if (dv != 0) { - /* If we've not already sent a tool_button_?? code, do - * so now. Then set FIRED_BIT so it won't be resent unless - * the user forces FIRED_BIT off. + if (dv) { + /* If the selected tool changed, reset the old + * tool key, and set the new one. */ - if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) { - input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting.toolMode), - 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; - } - - if (p != 0) { - input_report_key(inputdev, BTN_LEFT, left); - input_report_key(inputdev, BTN_MIDDLE, middle); - input_report_key(inputdev, BTN_RIGHT, right); + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); + input_report_key(inputdev, + aiptek->curSetting.toolMode, 1); + aiptek->previousToolMode = aiptek->curSetting.toolMode; } + } - /* For safety, we're sending key 'break' codes for the - * neighboring macro keys. - */ - if (macro > 0) { - input_report_key(inputdev, - macroKeyEvents[macro - 1], 0); - } - if (macro < 25) { - input_report_key(inputdev, - macroKeyEvents[macro + 1], 0); - } + if (aiptek->lastMacro != -1 && aiptek->lastMacro != macro) { + input_report_key(inputdev, macroKeyEvents[aiptek->lastMacro], 0); + aiptek->lastMacro = -1; + } + if (macro != -1 && macro != aiptek->lastMacro) { input_report_key(inputdev, macroKeyEvents[macro], 1); - input_report_rel(inputdev, ABS_MISC, - p | AIPTEK_REPORT_TOOL_MOUSE); - input_sync(inputdev); + aiptek->lastMacro = macro; } + + input_report_abs(inputdev, ABS_MISC, + p | AIPTEK_REPORT_TOOL_MOUSE); + input_sync(inputdev); } /* We have no idea which tool can generate a report 6. Theoretically, * neither need to, having been given reports 4 & 5 for such use. @@ -725,15 +766,18 @@ static void aiptek_irq(struct urb *urb) 0); } - /* If we've not already sent a tool_button_?? code, do - * so now. Then set FIRED_BIT so it won't be resent unless - * the user forces FIRED_BIT off. - */ - if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) { + /* If the selected tool changed, reset the old + tool key, and set the new one. + */ + if (aiptek->previousToolMode != + aiptek->curSetting.toolMode) { + input_report_key(inputdev, + aiptek->previousToolMode, 0); input_report_key(inputdev, - TOOL_BUTTON(aiptek->curSetting. - toolMode), 1); - aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT; + aiptek->curSetting.toolMode, + 1); + aiptek->previousToolMode = + aiptek->curSetting.toolMode; } input_report_key(inputdev, macroKeyEvents[macro], 1); @@ -1007,9 +1051,6 @@ static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "%dx%d\n", aiptek->inputdev->absmax[ABS_X] + 1, aiptek->inputdev->absmax[ABS_Y] + 1); @@ -1024,117 +1065,35 @@ static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL); /*********************************************************************** - * support routines for the 'product_id' file - */ -static ssize_t show_tabletProductId(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct aiptek *aiptek = dev_get_drvdata(dev); - - if (aiptek == NULL) - return 0; - - return snprintf(buf, PAGE_SIZE, "0x%04x\n", - aiptek->inputdev->id.product); -} - -static DEVICE_ATTR(product_id, S_IRUGO, show_tabletProductId, NULL); - -/*********************************************************************** - * support routines for the 'vendor_id' file - */ -static ssize_t show_tabletVendorId(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct aiptek *aiptek = dev_get_drvdata(dev); - - if (aiptek == NULL) - return 0; - - return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->inputdev->id.vendor); -} - -static DEVICE_ATTR(vendor_id, S_IRUGO, show_tabletVendorId, NULL); - -/*********************************************************************** - * support routines for the 'vendor' file - */ -static ssize_t show_tabletManufacturer(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct aiptek *aiptek = dev_get_drvdata(dev); - int retval; - - if (aiptek == NULL) - return 0; - - retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->manufacturer); - return retval; -} - -static DEVICE_ATTR(vendor, S_IRUGO, show_tabletManufacturer, NULL); - -/*********************************************************************** - * support routines for the 'product' file - */ -static ssize_t show_tabletProduct(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct aiptek *aiptek = dev_get_drvdata(dev); - int retval; - - if (aiptek == NULL) - return 0; - - retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->product); - return retval; -} - -static DEVICE_ATTR(product, S_IRUGO, show_tabletProduct, NULL); - -/*********************************************************************** * support routines for the 'pointer_mode' file. Note that this file * both displays current setting and allows reprogramming. */ +static struct aiptek_map pointer_mode_map[] = { + { "stylus", AIPTEK_POINTER_ONLY_STYLUS_MODE }, + { "mouse", AIPTEK_POINTER_ONLY_MOUSE_MODE }, + { "either", AIPTEK_POINTER_EITHER_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribute *attr, char *buf) { struct aiptek *aiptek = dev_get_drvdata(dev); - char *s; - - if (aiptek == NULL) - return 0; - - switch (aiptek->curSetting.pointerMode) { - case AIPTEK_POINTER_ONLY_STYLUS_MODE: - s = "stylus"; - break; - - case AIPTEK_POINTER_ONLY_MOUSE_MODE: - s = "mouse"; - break; - - case AIPTEK_POINTER_EITHER_MODE: - s = "either"; - break; - default: - s = "unknown"; - break; - } - return snprintf(buf, PAGE_SIZE, "%s\n", s); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(pointer_mode_map, + aiptek->curSetting.pointerMode)); } static ssize_t store_tabletPointerMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; + int new_mode = map_str_to_val(pointer_mode_map, buf, count); - if (strcmp(buf, "stylus") == 0) { - aiptek->newSetting.pointerMode = - AIPTEK_POINTER_ONLY_STYLUS_MODE; - } else if (strcmp(buf, "mouse") == 0) { - aiptek->newSetting.pointerMode = AIPTEK_POINTER_ONLY_MOUSE_MODE; - } else if (strcmp(buf, "either") == 0) { - aiptek->newSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE; - } + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.pointerMode = new_mode; return count; } @@ -1146,44 +1105,32 @@ static DEVICE_ATTR(pointer_mode, * support routines for the 'coordinate_mode' file. Note that this file * both displays current setting and allows reprogramming. */ + +static struct aiptek_map coordinate_mode_map[] = { + { "absolute", AIPTEK_COORDINATE_ABSOLUTE_MODE }, + { "relative", AIPTEK_COORDINATE_RELATIVE_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, char *buf) { struct aiptek *aiptek = dev_get_drvdata(dev); - char *s; - if (aiptek == NULL) - return 0; - - switch (aiptek->curSetting.coordinateMode) { - case AIPTEK_COORDINATE_ABSOLUTE_MODE: - s = "absolute"; - break; - - case AIPTEK_COORDINATE_RELATIVE_MODE: - s = "relative"; - break; - - default: - s = "unknown"; - break; - } - return snprintf(buf, PAGE_SIZE, "%s\n", s); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(coordinate_mode_map, + aiptek->curSetting.coordinateMode)); } static ssize_t store_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; + int new_mode = map_str_to_val(coordinate_mode_map, buf, count); - if (strcmp(buf, "absolute") == 0) { - aiptek->newSetting.pointerMode = - AIPTEK_COORDINATE_ABSOLUTE_MODE; - } else if (strcmp(buf, "relative") == 0) { - aiptek->newSetting.pointerMode = - AIPTEK_COORDINATE_RELATIVE_MODE; - } + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + + aiptek->newSetting.coordinateMode = new_mode; return count; } @@ -1195,73 +1142,37 @@ static DEVICE_ATTR(coordinate_mode, * support routines for the 'tool_mode' file. Note that this file * both displays current setting and allows reprogramming. */ + +static struct aiptek_map tool_mode_map[] = { + { "mouse", AIPTEK_TOOL_BUTTON_MOUSE_MODE }, + { "eraser", AIPTEK_TOOL_BUTTON_ERASER_MODE }, + { "pencil", AIPTEK_TOOL_BUTTON_PENCIL_MODE }, + { "pen", AIPTEK_TOOL_BUTTON_PEN_MODE }, + { "brush", AIPTEK_TOOL_BUTTON_BRUSH_MODE }, + { "airbrush", AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE }, + { "lens", AIPTEK_TOOL_BUTTON_LENS_MODE }, + { NULL, AIPTEK_INVALID_VALUE } +}; + static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *attr, char *buf) { struct aiptek *aiptek = dev_get_drvdata(dev); - char *s; - - if (aiptek == NULL) - return 0; - - switch (TOOL_BUTTON(aiptek->curSetting.toolMode)) { - case AIPTEK_TOOL_BUTTON_MOUSE_MODE: - s = "mouse"; - break; - - case AIPTEK_TOOL_BUTTON_ERASER_MODE: - s = "eraser"; - break; - - case AIPTEK_TOOL_BUTTON_PENCIL_MODE: - s = "pencil"; - break; - - case AIPTEK_TOOL_BUTTON_PEN_MODE: - s = "pen"; - break; - - case AIPTEK_TOOL_BUTTON_BRUSH_MODE: - s = "brush"; - break; - - case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE: - s = "airbrush"; - break; - - case AIPTEK_TOOL_BUTTON_LENS_MODE: - s = "lens"; - break; - default: - s = "unknown"; - break; - } - return snprintf(buf, PAGE_SIZE, "%s\n", s); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(tool_mode_map, + aiptek->curSetting.toolMode)); } static ssize_t store_tabletToolMode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; + int new_mode = map_str_to_val(tool_mode_map, buf, count); - if (strcmp(buf, "mouse") == 0) { - aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_MOUSE_MODE; - } else if (strcmp(buf, "eraser") == 0) { - aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_ERASER_MODE; - } else if (strcmp(buf, "pencil") == 0) { - aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PENCIL_MODE; - } else if (strcmp(buf, "pen") == 0) { - aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE; - } else if (strcmp(buf, "brush") == 0) { - aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_BRUSH_MODE; - } else if (strcmp(buf, "airbrush") == 0) { - aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE; - } else if (strcmp(buf, "lens") == 0) { - aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_LENS_MODE; - } + if (new_mode == AIPTEK_INVALID_VALUE) + return -EINVAL; + aiptek->newSetting.toolMode = new_mode; return count; } @@ -1277,9 +1188,6 @@ static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *att { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) { return snprintf(buf, PAGE_SIZE, "disable\n"); } else { @@ -1294,9 +1202,6 @@ store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char struct aiptek *aiptek = dev_get_drvdata(dev); int x; - if (aiptek == NULL) - return 0; - if (strcmp(buf, "disable") == 0) { aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE; } else { @@ -1319,9 +1224,6 @@ static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *att { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) { return snprintf(buf, PAGE_SIZE, "disable\n"); } else { @@ -1336,9 +1238,6 @@ store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char struct aiptek *aiptek = dev_get_drvdata(dev); int y; - if (aiptek == NULL) - return 0; - if (strcmp(buf, "disable") == 0) { aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE; } else { @@ -1361,9 +1260,6 @@ static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribut { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay); } @@ -1372,9 +1268,6 @@ store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - aiptek->newSetting.jitterDelay = (int)simple_strtol(buf, NULL, 10); return count; } @@ -1391,9 +1284,6 @@ static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_at { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.programmableDelay); } @@ -1403,9 +1293,6 @@ store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, NULL, 10); return count; } @@ -1415,23 +1302,6 @@ static DEVICE_ATTR(delay, show_tabletProgrammableDelay, store_tabletProgrammableDelay); /*********************************************************************** - * support routines for the 'input_path' file. Note that this file - * only displays current setting. - */ -static ssize_t show_tabletInputDevice(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct aiptek *aiptek = dev_get_drvdata(dev); - - if (aiptek == NULL) - return 0; - - return snprintf(buf, PAGE_SIZE, "/dev/input/%s\n", - aiptek->features.inputPath); -} - -static DEVICE_ATTR(input_path, S_IRUGO, show_tabletInputDevice, NULL); - -/*********************************************************************** * support routines for the 'event_count' file. Note that this file * only displays current setting. */ @@ -1439,9 +1309,6 @@ static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attri { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount); } @@ -1456,9 +1323,6 @@ static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_at struct aiptek *aiptek = dev_get_drvdata(dev); char *retMsg; - if (aiptek == NULL) - return 0; - switch (aiptek->diagnostic) { case AIPTEK_DIAGNOSTIC_NA: retMsg = "no errors\n"; @@ -1493,45 +1357,32 @@ static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL); * support routines for the 'stylus_upper' file. Note that this file * both displays current setting and allows for setting changing. */ + +static struct aiptek_map stylus_button_map[] = { + { "upper", AIPTEK_STYLUS_UPPER_BUTTON }, + { "lower", AIPTEK_STYLUS_LOWER_BUTTON }, + { NULL, AIPTEK_INVALID_VALUE } +}; + static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribute *attr, char *buf) { struct aiptek *aiptek = dev_get_drvdata(dev); - char *s; - - if (aiptek == NULL) - return 0; - - switch (aiptek->curSetting.stylusButtonUpper) { - case AIPTEK_STYLUS_UPPER_BUTTON: - s = "upper"; - break; - - case AIPTEK_STYLUS_LOWER_BUTTON: - s = "lower"; - break; - default: - s = "unknown"; - break; - } - return snprintf(buf, PAGE_SIZE, "%s\n", s); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(stylus_button_map, + aiptek->curSetting.stylusButtonUpper)); } static ssize_t store_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(stylus_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - if (strcmp(buf, "upper") == 0) { - aiptek->newSetting.stylusButtonUpper = - AIPTEK_STYLUS_UPPER_BUTTON; - } else if (strcmp(buf, "lower") == 0) { - aiptek->newSetting.stylusButtonUpper = - AIPTEK_STYLUS_LOWER_BUTTON; - } + aiptek->newSetting.stylusButtonUpper = new_button; return count; } @@ -1543,45 +1394,26 @@ static DEVICE_ATTR(stylus_upper, * support routines for the 'stylus_lower' file. Note that this file * both displays current setting and allows for setting changing. */ + static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribute *attr, char *buf) { struct aiptek *aiptek = dev_get_drvdata(dev); - char *s; - - if (aiptek == NULL) - return 0; - - switch (aiptek->curSetting.stylusButtonLower) { - case AIPTEK_STYLUS_UPPER_BUTTON: - s = "upper"; - break; - - case AIPTEK_STYLUS_LOWER_BUTTON: - s = "lower"; - break; - default: - s = "unknown"; - break; - } - return snprintf(buf, PAGE_SIZE, "%s\n", s); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(stylus_button_map, + aiptek->curSetting.stylusButtonLower)); } static ssize_t store_tabletStylusLower(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(stylus_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - if (strcmp(buf, "upper") == 0) { - aiptek->newSetting.stylusButtonLower = - AIPTEK_STYLUS_UPPER_BUTTON; - } else if (strcmp(buf, "lower") == 0) { - aiptek->newSetting.stylusButtonLower = - AIPTEK_STYLUS_LOWER_BUTTON; - } + aiptek->newSetting.stylusButtonLower = new_button; return count; } @@ -1593,49 +1425,33 @@ static DEVICE_ATTR(stylus_lower, * support routines for the 'mouse_left' file. Note that this file * both displays current setting and allows for setting changing. */ + +static struct aiptek_map mouse_button_map[] = { + { "left", AIPTEK_MOUSE_LEFT_BUTTON }, + { "middle", AIPTEK_MOUSE_MIDDLE_BUTTON }, + { "right", AIPTEK_MOUSE_RIGHT_BUTTON }, + { NULL, AIPTEK_INVALID_VALUE } +}; + static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute *attr, char *buf) { struct aiptek *aiptek = dev_get_drvdata(dev); - char *s; - - if (aiptek == NULL) - return 0; - - switch (aiptek->curSetting.mouseButtonLeft) { - case AIPTEK_MOUSE_LEFT_BUTTON: - s = "left"; - break; - - case AIPTEK_MOUSE_MIDDLE_BUTTON: - s = "middle"; - break; - - case AIPTEK_MOUSE_RIGHT_BUTTON: - s = "right"; - break; - default: - s = "unknown"; - break; - } - return snprintf(buf, PAGE_SIZE, "%s\n", s); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonLeft)); } static ssize_t store_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(mouse_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - if (strcmp(buf, "left") == 0) { - aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON; - } else if (strcmp(buf, "middle") == 0) { - aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_MIDDLE_BUTTON; - } else if (strcmp(buf, "right") == 0) { - aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_RIGHT_BUTTON; - } + aiptek->newSetting.mouseButtonLeft = new_button; return count; } @@ -1650,48 +1466,22 @@ static DEVICE_ATTR(mouse_left, static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, char *buf) { struct aiptek *aiptek = dev_get_drvdata(dev); - char *s; - if (aiptek == NULL) - return 0; - - switch (aiptek->curSetting.mouseButtonMiddle) { - case AIPTEK_MOUSE_LEFT_BUTTON: - s = "left"; - break; - - case AIPTEK_MOUSE_MIDDLE_BUTTON: - s = "middle"; - break; - - case AIPTEK_MOUSE_RIGHT_BUTTON: - s = "right"; - break; - - default: - s = "unknown"; - break; - } - return snprintf(buf, PAGE_SIZE, "%s\n", s); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonMiddle)); } static ssize_t store_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(mouse_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - if (strcmp(buf, "left") == 0) { - aiptek->newSetting.mouseButtonMiddle = AIPTEK_MOUSE_LEFT_BUTTON; - } else if (strcmp(buf, "middle") == 0) { - aiptek->newSetting.mouseButtonMiddle = - AIPTEK_MOUSE_MIDDLE_BUTTON; - } else if (strcmp(buf, "right") == 0) { - aiptek->newSetting.mouseButtonMiddle = - AIPTEK_MOUSE_RIGHT_BUTTON; - } + aiptek->newSetting.mouseButtonMiddle = new_button; return count; } @@ -1706,47 +1496,22 @@ static DEVICE_ATTR(mouse_middle, static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute *attr, char *buf) { struct aiptek *aiptek = dev_get_drvdata(dev); - char *s; - - if (aiptek == NULL) - return 0; - - switch (aiptek->curSetting.mouseButtonRight) { - case AIPTEK_MOUSE_LEFT_BUTTON: - s = "left"; - break; - - case AIPTEK_MOUSE_MIDDLE_BUTTON: - s = "middle"; - break; - case AIPTEK_MOUSE_RIGHT_BUTTON: - s = "right"; - break; - - default: - s = "unknown"; - break; - } - return snprintf(buf, PAGE_SIZE, "%s\n", s); + return snprintf(buf, PAGE_SIZE, "%s\n", + map_val_to_str(mouse_button_map, + aiptek->curSetting.mouseButtonRight)); } static ssize_t store_tabletMouseRight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); + int new_button = map_str_to_val(mouse_button_map, buf, count); - if (aiptek == NULL) - return 0; + if (new_button == AIPTEK_INVALID_VALUE) + return -EINVAL; - if (strcmp(buf, "left") == 0) { - aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_LEFT_BUTTON; - } else if (strcmp(buf, "middle") == 0) { - aiptek->newSetting.mouseButtonRight = - AIPTEK_MOUSE_MIDDLE_BUTTON; - } else if (strcmp(buf, "right") == 0) { - aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON; - } + aiptek->newSetting.mouseButtonRight = new_button; return count; } @@ -1762,9 +1527,6 @@ static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *att { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) { return snprintf(buf, PAGE_SIZE, "disable\n"); } else { @@ -1778,9 +1540,6 @@ store_tabletWheel(struct device *dev, struct device_attribute *attr, const char { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10); return count; } @@ -1794,11 +1553,6 @@ static DEVICE_ATTR(wheel, */ static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *attr, char *buf) { - struct aiptek *aiptek = dev_get_drvdata(dev); - - if (aiptek == NULL) - return 0; - /* There is nothing useful to display, so a one-line manual * is in order... */ @@ -1811,9 +1565,6 @@ store_tabletExecute(struct device *dev, struct device_attribute *attr, const cha { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - /* We do not care what you write to this file. Merely the action * of writing to this file triggers a tablet reprogramming. */ @@ -1837,9 +1588,6 @@ static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *a { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode); } @@ -1853,9 +1601,6 @@ static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode); } @@ -1869,86 +1614,39 @@ static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *at { struct aiptek *aiptek = dev_get_drvdata(dev); - if (aiptek == NULL) - return 0; - return snprintf(buf, PAGE_SIZE, "%04x\n", aiptek->features.firmwareCode); } static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL); -/*********************************************************************** - * This routine removes all existing sysfs files managed by this device - * driver. - */ -static void aiptek_delete_files(struct device *dev) -{ - device_remove_file(dev, &dev_attr_size); - device_remove_file(dev, &dev_attr_product_id); - device_remove_file(dev, &dev_attr_vendor_id); - device_remove_file(dev, &dev_attr_vendor); - device_remove_file(dev, &dev_attr_product); - device_remove_file(dev, &dev_attr_pointer_mode); - device_remove_file(dev, &dev_attr_coordinate_mode); - device_remove_file(dev, &dev_attr_tool_mode); - device_remove_file(dev, &dev_attr_xtilt); - device_remove_file(dev, &dev_attr_ytilt); - device_remove_file(dev, &dev_attr_jitter); - device_remove_file(dev, &dev_attr_delay); - device_remove_file(dev, &dev_attr_input_path); - device_remove_file(dev, &dev_attr_event_count); - device_remove_file(dev, &dev_attr_diagnostic); - device_remove_file(dev, &dev_attr_odm_code); - device_remove_file(dev, &dev_attr_model_code); - device_remove_file(dev, &dev_attr_firmware_code); - device_remove_file(dev, &dev_attr_stylus_lower); - device_remove_file(dev, &dev_attr_stylus_upper); - device_remove_file(dev, &dev_attr_mouse_left); - device_remove_file(dev, &dev_attr_mouse_middle); - device_remove_file(dev, &dev_attr_mouse_right); - device_remove_file(dev, &dev_attr_wheel); - device_remove_file(dev, &dev_attr_execute); -} - -/*********************************************************************** - * This routine creates the sysfs files managed by this device - * driver. - */ -static int aiptek_add_files(struct device *dev) -{ - int ret; +static struct attribute *aiptek_attributes[] = { + &dev_attr_size.attr, + &dev_attr_pointer_mode.attr, + &dev_attr_coordinate_mode.attr, + &dev_attr_tool_mode.attr, + &dev_attr_xtilt.attr, + &dev_attr_ytilt.attr, + &dev_attr_jitter.attr, + &dev_attr_delay.attr, + &dev_attr_event_count.attr, + &dev_attr_diagnostic.attr, + &dev_attr_odm_code.attr, + &dev_attr_model_code.attr, + &dev_attr_firmware_code.attr, + &dev_attr_stylus_lower.attr, + &dev_attr_stylus_upper.attr, + &dev_attr_mouse_left.attr, + &dev_attr_mouse_middle.attr, + &dev_attr_mouse_right.attr, + &dev_attr_wheel.attr, + &dev_attr_execute.attr, + NULL +}; - if ((ret = device_create_file(dev, &dev_attr_size)) || - (ret = device_create_file(dev, &dev_attr_product_id)) || - (ret = device_create_file(dev, &dev_attr_vendor_id)) || - (ret = device_create_file(dev, &dev_attr_vendor)) || - (ret = device_create_file(dev, &dev_attr_product)) || - (ret = device_create_file(dev, &dev_attr_pointer_mode)) || - (ret = device_create_file(dev, &dev_attr_coordinate_mode)) || - (ret = device_create_file(dev, &dev_attr_tool_mode)) || - (ret = device_create_file(dev, &dev_attr_xtilt)) || - (ret = device_create_file(dev, &dev_attr_ytilt)) || - (ret = device_create_file(dev, &dev_attr_jitter)) || - (ret = device_create_file(dev, &dev_attr_delay)) || - (ret = device_create_file(dev, &dev_attr_input_path)) || - (ret = device_create_file(dev, &dev_attr_event_count)) || - (ret = device_create_file(dev, &dev_attr_diagnostic)) || - (ret = device_create_file(dev, &dev_attr_odm_code)) || - (ret = device_create_file(dev, &dev_attr_model_code)) || - (ret = device_create_file(dev, &dev_attr_firmware_code)) || - (ret = device_create_file(dev, &dev_attr_stylus_lower)) || - (ret = device_create_file(dev, &dev_attr_stylus_upper)) || - (ret = device_create_file(dev, &dev_attr_mouse_left)) || - (ret = device_create_file(dev, &dev_attr_mouse_middle)) || - (ret = device_create_file(dev, &dev_attr_mouse_right)) || - (ret = device_create_file(dev, &dev_attr_wheel)) || - (ret = device_create_file(dev, &dev_attr_execute))) { - err("aiptek: killing own sysfs device files\n"); - aiptek_delete_files(dev); - } - return ret; -} +static struct attribute_group aiptek_attribute_group = { + .attrs = aiptek_attributes, +}; /*********************************************************************** * This routine is called when a tablet has been identified. It basically @@ -1961,8 +1659,6 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_endpoint_descriptor *endpoint; struct aiptek *aiptek; struct input_dev *inputdev; - struct input_handle *inputhandle; - struct list_head *node, *next; int i; int speeds[] = { 0, AIPTEK_PROGRAMMABLE_DELAY_50, @@ -1984,17 +1680,23 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) aiptek = kzalloc(sizeof(struct aiptek), GFP_KERNEL); inputdev = input_allocate_device(); - if (!aiptek || !inputdev) + if (!aiptek || !inputdev) { + warn("aiptek: cannot allocate memory or input device"); goto fail1; + } aiptek->data = usb_buffer_alloc(usbdev, AIPTEK_PACKET_LENGTH, GFP_ATOMIC, &aiptek->data_dma); - if (!aiptek->data) + if (!aiptek->data) { + warn("aiptek: cannot allocate usb buffer"); goto fail1; + } aiptek->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!aiptek->urb) + if (!aiptek->urb) { + warn("aiptek: cannot allocate urb"); goto fail2; + } aiptek->inputdev = inputdev; aiptek->usbdev = usbdev; @@ -2002,6 +1704,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) aiptek->inDelay = 0; aiptek->endDelay = 0; aiptek->previousJitterable = 0; + aiptek->lastMacro = -1; /* Set up the curSettings struct. Said struct contains the current * programmable parameters. The newSetting struct contains changes @@ -2054,36 +1757,23 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) /* Now program the capacities of the tablet, in terms of being * an input device. */ - inputdev->evbit[0] |= BIT(EV_KEY) - | BIT(EV_ABS) - | BIT(EV_REL) - | BIT(EV_MSC); - - inputdev->absbit[0] |= BIT(ABS_MISC); + for (i = 0; i < ARRAY_SIZE(eventTypes); ++i) + __set_bit(eventTypes[i], inputdev->evbit); - inputdev->relbit[0] |= - (BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) | BIT(REL_MISC)); + for (i = 0; i < ARRAY_SIZE(absEvents); ++i) + __set_bit(absEvents[i], inputdev->absbit); - inputdev->keybit[LONG(BTN_LEFT)] |= - (BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE)); + for (i = 0; i < ARRAY_SIZE(relEvents); ++i) + __set_bit(relEvents[i], inputdev->relbit); - inputdev->keybit[LONG(BTN_DIGI)] |= - (BIT(BTN_TOOL_PEN) | - BIT(BTN_TOOL_RUBBER) | - BIT(BTN_TOOL_PENCIL) | - BIT(BTN_TOOL_AIRBRUSH) | - BIT(BTN_TOOL_BRUSH) | - BIT(BTN_TOOL_MOUSE) | - BIT(BTN_TOOL_LENS) | - BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2)); + __set_bit(MSC_SERIAL, inputdev->mscbit); - inputdev->mscbit[0] = BIT(MSC_SERIAL); + /* Set up key and button codes */ + for (i = 0; i < ARRAY_SIZE(buttonEvents); ++i) + __set_bit(buttonEvents[i], inputdev->keybit); - /* Programming the tablet macro keys needs to be done with a for loop - * as the keycodes are discontiguous. - */ for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i) - set_bit(macroKeyEvents[i], inputdev->keybit); + __set_bit(macroKeyEvents[i], inputdev->keybit); /* * Program the input device coordinate capacities. We do not yet @@ -2134,25 +1824,11 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) } } - /* Register the tablet as an Input Device - */ - err = input_register_device(aiptek->inputdev); - if (err) + /* Murphy says that some day someone will have a tablet that fails the + above test. That's you, Frederic Rodrigo */ + if (i == ARRAY_SIZE(speeds)) { + info("input: Aiptek tried all speeds, no sane response"); goto fail2; - - /* We now will look for the evdev device which is mapped to - * the tablet. The partial name is kept in the link list of - * input_handles associated with this input device. - * What identifies an evdev input_handler is that it begins - * with 'event', continues with a digit, and that in turn - * is mapped to input/eventN. - */ - list_for_each_safe(node, next, &inputdev->h_list) { - inputhandle = to_handle(node); - if (strncmp(inputhandle->name, "event", 5) == 0) { - strcpy(aiptek->features.inputPath, inputhandle->name); - break; - } } /* Associate this driver's struct with the usb interface. @@ -2161,18 +1837,27 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) /* Set up the sysfs files */ - aiptek_add_files(&intf->dev); + err = sysfs_create_group(&intf->dev.kobj, &aiptek_attribute_group); + if (err) { + warn("aiptek: cannot create sysfs group err: %d", err); + goto fail3; + } - /* Make sure the evdev module is loaded. Assuming evdev IS a module :-) + /* Register the tablet as an Input Device */ - if (request_module("evdev") != 0) - info("aiptek: error loading 'evdev' module"); - + err = input_register_device(aiptek->inputdev); + if (err) { + warn("aiptek: input_register_device returned err: %d", err); + goto fail4; + } return 0; + fail4: sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); + fail3: usb_free_urb(aiptek->urb); fail2: usb_buffer_free(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data, aiptek->data_dma); - fail1: input_free_device(inputdev); + fail1: usb_set_intfdata(intf, NULL); + input_free_device(inputdev); kfree(aiptek); return err; } @@ -2192,7 +1877,7 @@ static void aiptek_disconnect(struct usb_interface *intf) */ usb_kill_urb(aiptek->urb); input_unregister_device(aiptek->inputdev); - aiptek_delete_files(&intf->dev); + sysfs_remove_group(&intf->dev.kobj, &aiptek_attribute_group); usb_free_urb(aiptek->urb); usb_buffer_free(interface_to_usbdev(intf), AIPTEK_PACKET_LENGTH, diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index ef01a807ec0..6542edb6f76 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -11,7 +11,7 @@ * Copyright (c) 2000 Daniel Egger <egger@suse.de> * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com> * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be> - * Copyright (c) 2002-2006 Ping Cheng <pingc@wacom.com> + * Copyright (c) 2002-2007 Ping Cheng <pingc@wacom.com> * * ChangeLog: * v0.1 (vp) - Initial release @@ -62,8 +62,9 @@ * - Minor data report fix * v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c, * - where wacom_sys.c deals with system specific code, - * - and wacom_wac.c deals with Wacom specific code + * - and wacom_wac.c deals with Wacom specific code * - Support Intuos3 4x6 + * v1.47 (pc) - Added support for Bamboo */ /* @@ -84,7 +85,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.46" +#define DRIVER_VERSION "v1.47" #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" #define DRIVER_LICENSE "GPL" @@ -123,6 +124,7 @@ extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wa extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac); extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac); extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac); +extern void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac); extern __u16 wacom_le16_to_cpu(unsigned char *data); extern __u16 wacom_be16_to_cpu(unsigned char *data); extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id); diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 83bddef6606..064e123c9b7 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -138,6 +138,12 @@ static void wacom_close(struct input_dev *dev) usb_kill_urb(wacom->irq); } +void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +{ + input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_1) | BIT(BTN_5); + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); +} + void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { input_dev->evbit[0] |= BIT(EV_MSC); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 7661f03a2db..fc03ba256f4 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -178,7 +178,8 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) case 2: /* Mouse with wheel */ wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04); - if (wacom->features->type == WACOM_G4) { + if (wacom->features->type == WACOM_G4 || + wacom->features->type == WACOM_MO) { rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03); wacom_report_rel(wcombo, REL_WHEEL, -rw); } else @@ -190,7 +191,8 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) id = CURSOR_DEVICE_ID; wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01); wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02); - if (wacom->features->type == WACOM_G4) + if (wacom->features->type == WACOM_G4 || + wacom->features->type == WACOM_MO) wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f); else wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f); @@ -226,7 +228,8 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) } /* send pad data */ - if (wacom->features->type == WACOM_G4) { + switch (wacom->features->type) { + case WACOM_G4: if (data[7] & 0xf8) { wacom_input_sync(wcombo); /* sync last event */ wacom->id[1] = 1; @@ -247,6 +250,33 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) wacom_report_abs(wcombo, ABS_MISC, 0); wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); } + break; + case WACOM_MO: + if ((data[7] & 0xf8) || (data[8] & 0x80)) { + wacom_input_sync(wcombo); /* sync last event */ + wacom->id[1] = 1; + wacom->serial[1] = (data[7] & 0xf8); + wacom_report_key(wcombo, BTN_0, (data[7] & 0x08)); + wacom_report_key(wcombo, BTN_1, (data[7] & 0x20)); + wacom_report_key(wcombo, BTN_4, (data[7] & 0x10)); + wacom_report_key(wcombo, BTN_5, (data[7] & 0x40)); + wacom_report_abs(wcombo, ABS_WHEEL, (data[8] & 0x7f)); + wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0); + wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); + } else if (wacom->id[1]) { + wacom_input_sync(wcombo); /* sync last event */ + wacom->id[1] = 0; + wacom_report_key(wcombo, BTN_0, (data[7] & 0x08)); + wacom_report_key(wcombo, BTN_1, (data[7] & 0x20)); + wacom_report_key(wcombo, BTN_4, (data[7] & 0x10)); + wacom_report_key(wcombo, BTN_5, (data[7] & 0x40)); + wacom_report_abs(wcombo, ABS_WHEEL, (data[8] & 0x7f)); + wacom_report_key(wcombo, BTN_TOOL_FINGER, 0); + wacom_report_abs(wcombo, ABS_MISC, 0); + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); + } + break; } return 1; } @@ -331,7 +361,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) wacom_report_key(wcombo, BTN_EXTRA, 0); wacom_report_abs(wcombo, ABS_THROTTLE, 0); wacom_report_abs(wcombo, ABS_RZ, 0); - } else { + } else { wacom_report_abs(wcombo, ABS_PRESSURE, 0); wacom_report_abs(wcombo, ABS_TILT_X, 0); wacom_report_abs(wcombo, ABS_TILT_Y, 0); @@ -423,9 +453,9 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) return result-1; /* Only large I3 and I1 & I2 support Lense Cursor */ - if((wacom->tool[idx] == BTN_TOOL_LENS) + if ((wacom->tool[idx] == BTN_TOOL_LENS) && ((wacom->features->type == INTUOS3) - || (wacom->features->type == INTUOS3S))) + || (wacom->features->type == INTUOS3S))) return 0; /* Cintiq doesn't send data when RDY bit isn't set */ @@ -517,6 +547,7 @@ int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo) break; case WACOM_G4: case GRAPHIRE: + case WACOM_MO: return (wacom_graphire_irq(wacom_wac, wcombo)); break; case PTU: @@ -538,6 +569,8 @@ int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo) void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { switch (wacom_wac->features->type) { + case WACOM_MO: + input_dev_mo(input_dev, wacom_wac); case WACOM_G4: input_dev_g4(input_dev, wacom_wac); /* fall through */ @@ -579,6 +612,7 @@ static struct wacom_features wacom_features[] = { { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE }, { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE }, { "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE }, + { "Wacom Bamboo", 9, 14760, 9225, 511, 63, WACOM_MO }, { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS }, { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS }, @@ -627,6 +661,7 @@ static struct usb_device_id wacom_ids[] = { { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x65) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index a5e12e8756d..a302e229bb8 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -25,6 +25,7 @@ enum { INTUOS3, INTUOS3L, CINTIQ, + WACOM_MO, MAX_TYPE }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index e5cca9bd040..69371779806 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -177,6 +177,7 @@ config TOUCHSCREEN_USB_COMPOSITE - some other eTurboTouch - Gunze AHL61 - DMC TSC-10/25 + - IRTOUCHSYSTEMS/UNITOP Have a look at <http://linux.chapter7.ch/touchkit/> for a usage description and the required user-space stuff. @@ -219,4 +220,9 @@ config TOUCHSCREEN_USB_DMC_TSC10 bool "DMC TSC-10/25 device support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_USB_IRTOUCH + default y + bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + endif diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index e3f22852bd0..b407028ffc5 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -9,6 +9,7 @@ * - eTurboTouch * - Gunze AHL61 * - DMC TSC-10/25 + * - IRTOUCHSYSTEMS/UNITOP * * Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) @@ -110,6 +111,7 @@ enum { DEVTYPE_ETURBO, DEVTYPE_GUNZE, DEVTYPE_DMC_TSC10, + DEVTYPE_IRTOUCH, }; static struct usb_device_id usbtouch_devices[] = { @@ -150,6 +152,11 @@ static struct usb_device_id usbtouch_devices[] = { {USB_DEVICE(0x0afa, 0x03e8), .driver_info = DEVTYPE_DMC_TSC10}, #endif +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH + {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, + {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, +#endif + {} }; @@ -416,6 +423,21 @@ static int dmc_tsc10_read_data(struct usbtouch_usb *dev, unsigned char *pkt) /***************************************************************************** + * IRTOUCH Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH +static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = (pkt[3] << 8) | pkt[2]; + dev->y = (pkt[5] << 8) | pkt[4]; + dev->touch = (pkt[1] & 0x03) ? 1 : 0; + + return 1; +} +#endif + + +/***************************************************************************** * the different device descriptors */ static struct usbtouch_device_info usbtouch_dev_info[] = { @@ -504,6 +526,17 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .read_data = dmc_tsc10_read_data, }, #endif + +#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH + [DEVTYPE_IRTOUCH] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 8, + .read_data = irtouch_read_data, + }, +#endif }; diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index 2db364898e1..d2f882e98e5 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c @@ -109,9 +109,11 @@ struct tsdev { int open; int minor; char name[8]; + struct input_handle handle; wait_queue_head_t wait; struct list_head client_list; - struct input_handle handle; + struct device dev; + int x, y, pressure; struct ts_calibration cal; }; @@ -163,9 +165,13 @@ static int tsdev_open(struct inode *inode, struct file *file) if (!tsdev || !tsdev->exist) return -ENODEV; + get_device(&tsdev->dev); + client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL); - if (!client) - return -ENOMEM; + if (!client) { + error = -ENOMEM; + goto err_put_tsdev; + } client->tsdev = tsdev; client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0; @@ -173,19 +179,25 @@ static int tsdev_open(struct inode *inode, struct file *file) if (!tsdev->open++ && tsdev->exist) { error = input_open_device(&tsdev->handle); - if (error) { - list_del(&client->node); - kfree(client); - return error; - } + if (error) + goto err_free_client; } file->private_data = client; return 0; + + err_free_client: + list_del(&client->node); + kfree(client); + err_put_tsdev: + put_device(&tsdev->dev); + return error; } -static void tsdev_free(struct tsdev *tsdev) +static void tsdev_free(struct device *dev) { + struct tsdev *tsdev = container_of(dev, struct tsdev, dev); + tsdev_table[tsdev->minor] = NULL; kfree(tsdev); } @@ -200,12 +212,10 @@ static int tsdev_release(struct inode *inode, struct file *file) list_del(&client->node); kfree(client); - if (!--tsdev->open) { - if (tsdev->exist) - input_close_device(&tsdev->handle); - else - tsdev_free(tsdev); - } + if (!--tsdev->open && tsdev->exist) + input_close_device(&tsdev->handle); + + put_device(&tsdev->dev); return 0; } @@ -361,7 +371,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, int x, y, tmp; do_gettimeofday(&time); - client->event[client->head].millisecs = time.tv_usec / 100; + client->event[client->head].millisecs = time.tv_usec / 1000; client->event[client->head].pressure = tsdev->pressure; x = tsdev->x; @@ -388,8 +398,6 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct tsdev *tsdev; - struct class_device *cdev; - dev_t devt; int minor, delta; int error; @@ -407,14 +415,13 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, INIT_LIST_HEAD(&tsdev->client_list); init_waitqueue_head(&tsdev->wait); - sprintf(tsdev->name, "ts%d", minor); - tsdev->exist = 1; tsdev->minor = minor; tsdev->handle.dev = dev; tsdev->handle.name = tsdev->name; tsdev->handle.handler = handler; tsdev->handle.private = tsdev; + snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor); /* Precompute the rough calibration matrix */ delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1; @@ -429,36 +436,30 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, tsdev->cal.yscale = (yres << 8) / delta; tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8); - tsdev_table[minor] = tsdev; - - devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), + snprintf(tsdev->dev.bus_id, sizeof(tsdev->dev.bus_id), + "ts%d", minor); + tsdev->dev.class = &input_class; + tsdev->dev.parent = &dev->dev; + tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor); + tsdev->dev.release = tsdev_free; + device_initialize(&tsdev->dev); - cdev = class_device_create(&input_class, &dev->cdev, devt, - dev->cdev.dev, tsdev->name); - if (IS_ERR(cdev)) { - error = PTR_ERR(cdev); - goto err_free_tsdev; - } + tsdev_table[minor] = tsdev; - /* temporary symlink to keep userspace happy */ - error = sysfs_create_link(&input_class.subsys.kobj, - &cdev->kobj, tsdev->name); + error = device_add(&tsdev->dev); if (error) - goto err_cdev_destroy; + goto err_free_tsdev; error = input_register_handle(&tsdev->handle); if (error) - goto err_remove_link; + goto err_delete_tsdev; return 0; - err_remove_link: - sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); - err_cdev_destroy: - class_device_destroy(&input_class, devt); + err_delete_tsdev: + device_del(&tsdev->dev); err_free_tsdev: - tsdev_table[minor] = NULL; - kfree(tsdev); + put_device(&tsdev->dev); return error; } @@ -468,10 +469,8 @@ static void tsdev_disconnect(struct input_handle *handle) struct tsdev_client *client; input_unregister_handle(handle); + device_del(&tsdev->dev); - sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); - class_device_destroy(&input_class, - MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + tsdev->minor)); tsdev->exist = 0; if (tsdev->open) { @@ -479,8 +478,9 @@ static void tsdev_disconnect(struct input_handle *handle) list_for_each_entry(client, &tsdev->client_list, node) kill_fasync(&client->fasync, SIGIO, POLL_HUP); wake_up_interruptible(&tsdev->wait); - } else - tsdev_free(tsdev); + } + + put_device(&tsdev->dev); } static const struct input_device_id tsdev_ids[] = { diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig new file mode 100644 index 00000000000..ab9c3e5a7c1 --- /dev/null +++ b/drivers/power/Kconfig @@ -0,0 +1,51 @@ +menuconfig POWER_SUPPLY + tristate "Power supply class support" + help + Say Y here to enable power supply class support. This allows + power supply (batteries, AC, USB) monitoring by userspace + via sysfs and uevent (if available) and/or APM kernel interface + (if selected below). + +if POWER_SUPPLY + +config POWER_SUPPLY_DEBUG + bool "Power supply debug" + help + Say Y here to enable debugging messages for power supply class + and drivers. + +config PDA_POWER + tristate "Generic PDA/phone power driver" + help + Say Y here to enable generic power driver for PDAs and phones with + one or two external power supplies (AC/USB) connected to main and + backup batteries, and optional builtin charger. + +config APM_POWER + tristate "APM emulation for class batteries" + depends on APM_EMULATION + help + Say Y here to enable support APM status emulation using + battery class devices. + +config BATTERY_DS2760 + tristate "DS2760 battery driver (HP iPAQ & others)" + select W1 + select W1_SLAVE_DS2760 + help + Say Y here to enable support for batteries with ds2760 chip. + +config BATTERY_PMU + tristate "Apple PMU battery" + depends on ADB_PMU + help + Say Y here to expose battery information on Apple machines + through the generic battery class. + +config BATTERY_OLPC + tristate "One Laptop Per Child battery" + depends on X86_32 && OLPC + help + Say Y to enable support for the battery on the OLPC laptop. + +endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile new file mode 100644 index 00000000000..6413ded5fe5 --- /dev/null +++ b/drivers/power/Makefile @@ -0,0 +1,22 @@ +power_supply-objs := power_supply_core.o + +ifeq ($(CONFIG_SYSFS),y) +power_supply-objs += power_supply_sysfs.o +endif + +ifeq ($(CONFIG_LEDS_TRIGGERS),y) +power_supply-objs += power_supply_leds.o +endif + +ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_POWER_SUPPLY) += power_supply.o + +obj-$(CONFIG_PDA_POWER) += pda_power.o +obj-$(CONFIG_APM_POWER) += apm_power.o + +obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o +obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o +obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c new file mode 100644 index 00000000000..042bd950d03 --- /dev/null +++ b/drivers/power/apm_power.c @@ -0,0 +1,243 @@ +/* + * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> + * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru> + * + * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru> + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + */ + +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/apm-emulation.h> + +#define PSY_PROP(psy, prop, val) psy->get_property(psy, \ + POWER_SUPPLY_PROP_##prop, val) + +#define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \ + prop, val) + +#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val) + +static struct power_supply *main_battery; + +static void find_main_battery(void) +{ + struct device *dev; + struct power_supply *bat, *batm; + union power_supply_propval full; + int max_charge = 0; + + main_battery = NULL; + batm = NULL; + list_for_each_entry(dev, &power_supply_class->devices, node) { + bat = dev_get_drvdata(dev); + /* If none of battery devices cantains 'use_for_apm' flag, + choice one with maximum design charge */ + if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) { + if (full.intval > max_charge) { + batm = bat; + max_charge = full.intval; + } + } + + if (bat->use_for_apm) + main_battery = bat; + } + if (!main_battery) + main_battery = batm; + + return; +} + +static int calculate_time(int status) +{ + union power_supply_propval charge_full, charge_empty; + union power_supply_propval charge, I; + + if (MPSY_PROP(CHARGE_FULL, &charge_full)) { + /* if battery can't report this property, use design value */ + if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full)) + return -1; + } + + if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) { + /* if battery can't report this property, use design value */ + if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty)) + charge_empty.intval = 0; + } + + if (MPSY_PROP(CHARGE_AVG, &charge)) { + /* if battery can't report average value, use momentary */ + if (MPSY_PROP(CHARGE_NOW, &charge)) + return -1; + } + + if (MPSY_PROP(CURRENT_AVG, &I)) { + /* if battery can't report average value, use momentary */ + if (MPSY_PROP(CURRENT_NOW, &I)) + return -1; + } + + if (status == POWER_SUPPLY_STATUS_CHARGING) + return ((charge.intval - charge_full.intval) * 60L) / + I.intval; + else + return -((charge.intval - charge_empty.intval) * 60L) / + I.intval; +} + +static int calculate_capacity(int using_charge) +{ + enum power_supply_property full_prop, empty_prop; + enum power_supply_property full_design_prop, empty_design_prop; + enum power_supply_property now_prop, avg_prop; + union power_supply_propval empty, full, cur; + int ret; + + if (using_charge) { + full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; + empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; + empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; + now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; + avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; + } else { + full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; + empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; + full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; + empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; + now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; + avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; + } + + if (_MPSY_PROP(full_prop, &full)) { + /* if battery can't report this property, use design value */ + if (_MPSY_PROP(full_design_prop, &full)) + return -1; + } + + if (_MPSY_PROP(avg_prop, &cur)) { + /* if battery can't report average value, use momentary */ + if (_MPSY_PROP(now_prop, &cur)) + return -1; + } + + if (_MPSY_PROP(empty_prop, &empty)) { + /* if battery can't report this property, use design value */ + if (_MPSY_PROP(empty_design_prop, &empty)) + empty.intval = 0; + } + + if (full.intval - empty.intval) + ret = ((cur.intval - empty.intval) * 100L) / + (full.intval - empty.intval); + else + return -1; + + if (ret > 100) + return 100; + else if (ret < 0) + return 0; + + return ret; +} + +static void apm_battery_apm_get_power_status(struct apm_power_info *info) +{ + union power_supply_propval status; + union power_supply_propval capacity, time_to_full, time_to_empty; + + down(&power_supply_class->sem); + find_main_battery(); + if (!main_battery) { + up(&power_supply_class->sem); + return; + } + + /* status */ + + if (MPSY_PROP(STATUS, &status)) + status.intval = POWER_SUPPLY_STATUS_UNKNOWN; + + /* ac line status */ + + if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) || + (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) || + (status.intval == POWER_SUPPLY_STATUS_FULL)) + info->ac_line_status = APM_AC_ONLINE; + else + info->ac_line_status = APM_AC_OFFLINE; + + /* battery life (i.e. capacity, in percents) */ + + if (MPSY_PROP(CAPACITY, &capacity) == 0) { + info->battery_life = capacity.intval; + } else { + /* try calculate using energy */ + info->battery_life = calculate_capacity(0); + /* if failed try calculate using charge instead */ + if (info->battery_life == -1) + info->battery_life = calculate_capacity(1); + } + + /* charging status */ + + if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { + info->battery_status = APM_BATTERY_STATUS_CHARGING; + } else { + if (info->battery_life > 50) + info->battery_status = APM_BATTERY_STATUS_HIGH; + else if (info->battery_life > 5) + info->battery_status = APM_BATTERY_STATUS_LOW; + else + info->battery_status = APM_BATTERY_STATUS_CRITICAL; + } + info->battery_flag = info->battery_status; + + /* time */ + + info->units = APM_UNITS_MINS; + + if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { + if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) { + if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) + info->time = calculate_time(status.intval); + else + info->time = time_to_full.intval / 60; + } + } else { + if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) { + if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) + info->time = calculate_time(status.intval); + else + info->time = time_to_empty.intval / 60; + } + } + + up(&power_supply_class->sem); + return; +} + +static int __init apm_battery_init(void) +{ + printk(KERN_INFO "APM Battery Driver\n"); + + apm_get_power_status = apm_battery_apm_get_power_status; + return 0; +} + +static void __exit apm_battery_exit(void) +{ + apm_get_power_status = NULL; + return; +} + +module_init(apm_battery_init); +module_exit(apm_battery_exit); + +MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>"); +MODULE_DESCRIPTION("APM emulation driver for battery monitoring class"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c new file mode 100644 index 00000000000..00e1ea6f1de --- /dev/null +++ b/drivers/power/ds2760_battery.c @@ -0,0 +1,470 @@ +/* + * Driver for batteries with DS2760 chips inside. + * + * Copyright © 2007 Anton Vorontsov + * 2004-2007 Matt Reimer + * 2004 Szabolcs Gyurko + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * Author: Anton Vorontsov <cbou@mail.ru> + * February 2007 + * + * Matt Reimer <mreimer@vpop.net> + * April 2004, 2005, 2007 + * + * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> + * September 2004 + */ + +#include <linux/module.h> +#include <linux/param.h> +#include <linux/jiffies.h> +#include <linux/workqueue.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> + +#include "../w1/w1.h" +#include "../w1/slaves/w1_ds2760.h" + +struct ds2760_device_info { + struct device *dev; + + /* DS2760 data, valid after calling ds2760_battery_read_status() */ + unsigned long update_time; /* jiffies when data read */ + char raw[DS2760_DATA_SIZE]; /* raw DS2760 data */ + int voltage_raw; /* units of 4.88 mV */ + int voltage_uV; /* units of µV */ + int current_raw; /* units of 0.625 mA */ + int current_uA; /* units of µA */ + int accum_current_raw; /* units of 0.25 mAh */ + int accum_current_uAh; /* units of µAh */ + int temp_raw; /* units of 0.125 °C */ + int temp_C; /* units of 0.1 °C */ + int rated_capacity; /* units of µAh */ + int rem_capacity; /* percentage */ + int full_active_uAh; /* units of µAh */ + int empty_uAh; /* units of µAh */ + int life_sec; /* units of seconds */ + int charge_status; /* POWER_SUPPLY_STATUS_* */ + + int full_counter; + struct power_supply bat; + struct device *w1_dev; + struct workqueue_struct *monitor_wqueue; + struct delayed_work monitor_work; +}; + +static unsigned int cache_time = 1000; +module_param(cache_time, uint, 0644); +MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); + +/* Some batteries have their rated capacity stored a N * 10 mAh, while + * others use an index into this table. */ +static int rated_capacities[] = { + 0, + 920, /* Samsung */ + 920, /* BYD */ + 920, /* Lishen */ + 920, /* NEC */ + 1440, /* Samsung */ + 1440, /* BYD */ + 1440, /* Lishen */ + 1440, /* NEC */ + 2880, /* Samsung */ + 2880, /* BYD */ + 2880, /* Lishen */ + 2880 /* NEC */ +}; + +/* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C + * temp is in Celsius */ +static int battery_interpolate(int array[], int temp) +{ + int index, dt; + + if (temp <= 0) + return array[0]; + if (temp >= 40) + return array[4]; + + index = temp / 10; + dt = temp % 10; + + return array[index] + (((array[index + 1] - array[index]) * dt) / 10); +} + +static int ds2760_battery_read_status(struct ds2760_device_info *di) +{ + int ret, i, start, count, scale[5]; + + if (di->update_time && time_before(jiffies, di->update_time + + msecs_to_jiffies(cache_time))) + return 0; + + /* The first time we read the entire contents of SRAM/EEPROM, + * but after that we just read the interesting bits that change. */ + if (di->update_time == 0) { + start = 0; + count = DS2760_DATA_SIZE; + } else { + start = DS2760_VOLTAGE_MSB; + count = DS2760_TEMP_LSB - start + 1; + } + + ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count); + if (ret != count) { + dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n", + di->w1_dev); + return 1; + } + + di->update_time = jiffies; + + /* DS2760 reports voltage in units of 4.88mV, but the battery class + * reports in units of uV, so convert by multiplying by 4880. */ + di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) | + (di->raw[DS2760_VOLTAGE_LSB] >> 5); + di->voltage_uV = di->voltage_raw * 4880; + + /* DS2760 reports current in signed units of 0.625mA, but the battery + * class reports in units of µA, so convert by multiplying by 625. */ + di->current_raw = + (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) | + (di->raw[DS2760_CURRENT_LSB] >> 3); + di->current_uA = di->current_raw * 625; + + /* DS2760 reports accumulated current in signed units of 0.25mAh. */ + di->accum_current_raw = + (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) | + di->raw[DS2760_CURRENT_ACCUM_LSB]; + di->accum_current_uAh = di->accum_current_raw * 250; + + /* DS2760 reports temperature in signed units of 0.125°C, but the + * battery class reports in units of 1/10 °C, so we convert by + * multiplying by .125 * 10 = 1.25. */ + di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) | + (di->raw[DS2760_TEMP_LSB] >> 5); + di->temp_C = di->temp_raw + (di->temp_raw / 4); + + /* At least some battery monitors (e.g. HP iPAQ) store the battery's + * maximum rated capacity. */ + if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities)) + di->rated_capacity = rated_capacities[ + (unsigned int)di->raw[DS2760_RATED_CAPACITY]]; + else + di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10; + + di->rated_capacity *= 1000; /* convert to µAh */ + + /* Calculate the full level at the present temperature. */ + di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 | + di->raw[DS2760_ACTIVE_FULL + 1]; + + scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 | + di->raw[DS2760_ACTIVE_FULL + 1]; + for (i = 1; i < 5; i++) + scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i]; + + di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10); + di->full_active_uAh *= 1000; /* convert to µAh */ + + /* Calculate the empty level at the present temperature. */ + scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4]; + for (i = 3; i >= 0; i--) + scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i]; + + di->empty_uAh = battery_interpolate(scale, di->temp_C / 10); + di->empty_uAh *= 1000; /* convert to µAh */ + + /* From Maxim Application Note 131: remaining capacity = + * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */ + di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) / + (di->full_active_uAh - di->empty_uAh); + + if (di->rem_capacity < 0) + di->rem_capacity = 0; + if (di->rem_capacity > 100) + di->rem_capacity = 100; + + if (di->current_uA) + di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * + 3600L) / di->current_uA; + else + di->life_sec = 0; + + return 0; +} + +static void ds2760_battery_update_status(struct ds2760_device_info *di) +{ + int old_charge_status = di->charge_status; + + ds2760_battery_read_status(di); + + if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN) + di->full_counter = 0; + + if (power_supply_am_i_supplied(&di->bat)) { + if (di->current_uA > 10000) { + di->charge_status = POWER_SUPPLY_STATUS_CHARGING; + di->full_counter = 0; + } else if (di->current_uA < -5000) { + if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING) + dev_notice(di->dev, "not enough power to " + "charge\n"); + di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + di->full_counter = 0; + } else if (di->current_uA < 10000 && + di->charge_status != POWER_SUPPLY_STATUS_FULL) { + + /* Don't consider the battery to be full unless + * we've seen the current < 10 mA at least two + * consecutive times. */ + + di->full_counter++; + + if (di->full_counter < 2) { + di->charge_status = POWER_SUPPLY_STATUS_CHARGING; + } else { + unsigned char acr[2]; + int acr_val; + + /* acr is in units of 0.25 mAh */ + acr_val = di->full_active_uAh * 4L / 1000; + + acr[0] = acr_val >> 8; + acr[1] = acr_val & 0xff; + + if (w1_ds2760_write(di->w1_dev, acr, + DS2760_CURRENT_ACCUM_MSB, 2) < 2) + dev_warn(di->dev, + "ACR reset failed\n"); + + di->charge_status = POWER_SUPPLY_STATUS_FULL; + } + } + } else { + di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; + di->full_counter = 0; + } + + if (di->charge_status != old_charge_status) + power_supply_changed(&di->bat); + + return; +} + +static void ds2760_battery_work(struct work_struct *work) +{ + struct ds2760_device_info *di = container_of(work, + struct ds2760_device_info, monitor_work.work); + const int interval = HZ * 60; + + dev_dbg(di->dev, "%s\n", __FUNCTION__); + + ds2760_battery_update_status(di); + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); + + return; +} + +#define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \ + bat); + +static void ds2760_battery_external_power_changed(struct power_supply *psy) +{ + struct ds2760_device_info *di = to_ds2760_device_info(psy); + + dev_dbg(di->dev, "%s\n", __FUNCTION__); + + cancel_delayed_work(&di->monitor_work); + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); + + return; +} + +static int ds2760_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ds2760_device_info *di = to_ds2760_device_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = di->charge_status; + return 0; + default: + break; + } + + ds2760_battery_read_status(di); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = di->voltage_uV; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = di->current_uA; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = di->rated_capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = di->full_active_uAh; + break; + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + val->intval = di->empty_uAh; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = di->accum_current_uAh; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = di->temp_C; + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property ds2760_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_EMPTY, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_TEMP, +}; + +static int ds2760_battery_probe(struct platform_device *pdev) +{ + int retval = 0; + struct ds2760_device_info *di; + struct ds2760_platform_data *pdata; + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + retval = -ENOMEM; + goto di_alloc_failed; + } + + platform_set_drvdata(pdev, di); + + pdata = pdev->dev.platform_data; + di->dev = &pdev->dev; + di->w1_dev = pdev->dev.parent; + di->bat.name = pdev->dev.bus_id; + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat.properties = ds2760_battery_props; + di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); + di->bat.get_property = ds2760_battery_get_property; + di->bat.external_power_changed = + ds2760_battery_external_power_changed; + + di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; + + retval = power_supply_register(&pdev->dev, &di->bat); + if (retval) { + dev_err(di->dev, "failed to register battery"); + goto batt_failed; + } + + INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); + di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id); + if (!di->monitor_wqueue) { + retval = -ESRCH; + goto workqueue_failed; + } + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1); + + goto success; + +workqueue_failed: + power_supply_unregister(&di->bat); +batt_failed: + kfree(di); +di_alloc_failed: +success: + return retval; +} + +static int ds2760_battery_remove(struct platform_device *pdev) +{ + struct ds2760_device_info *di = platform_get_drvdata(pdev); + + cancel_rearming_delayed_workqueue(di->monitor_wqueue, + &di->monitor_work); + destroy_workqueue(di->monitor_wqueue); + power_supply_unregister(&di->bat); + + return 0; +} + +#ifdef CONFIG_PM + +static int ds2760_battery_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct ds2760_device_info *di = platform_get_drvdata(pdev); + + di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; + + return 0; +} + +static int ds2760_battery_resume(struct platform_device *pdev) +{ + struct ds2760_device_info *di = platform_get_drvdata(pdev); + + di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; + power_supply_changed(&di->bat); + + cancel_delayed_work(&di->monitor_work); + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ); + + return 0; +} + +#else + +#define ds2760_battery_suspend NULL +#define ds2760_battery_resume NULL + +#endif /* CONFIG_PM */ + +static struct platform_driver ds2760_battery_driver = { + .driver = { + .name = "ds2760-battery", + }, + .probe = ds2760_battery_probe, + .remove = ds2760_battery_remove, + .suspend = ds2760_battery_suspend, + .resume = ds2760_battery_resume, +}; + +static int __init ds2760_battery_init(void) +{ + return platform_driver_register(&ds2760_battery_driver); +} + +static void __exit ds2760_battery_exit(void) +{ + platform_driver_unregister(&ds2760_battery_driver); + return; +} + +module_init(ds2760_battery_init); +module_exit(ds2760_battery_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " + "Matt Reimer <mreimer@vpop.net>, " + "Anton Vorontsov <cbou@mail.ru>"); +MODULE_DESCRIPTION("ds2760 battery driver"); diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c new file mode 100644 index 00000000000..878684df766 --- /dev/null +++ b/drivers/power/olpc_battery.c @@ -0,0 +1,352 @@ +/* + * Battery driver for One Laptop Per Child board. + * + * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <asm/olpc.h> + + +#define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */ +#define EC_BAT_CURRENT 0x11 /* int16_t, *15.625/120, mA */ +#define EC_BAT_ACR 0x12 +#define EC_BAT_TEMP 0x13 /* uint16_t, *100/256, °C */ +#define EC_AMB_TEMP 0x14 /* uint16_t, *100/256, °C */ +#define EC_BAT_STATUS 0x15 /* uint8_t, bitmask */ +#define EC_BAT_SOC 0x16 /* uint8_t, percentage */ +#define EC_BAT_SERIAL 0x17 /* uint8_t[6] */ +#define EC_BAT_EEPROM 0x18 /* uint8_t adr as input, uint8_t output */ +#define EC_BAT_ERRCODE 0x1f /* uint8_t, bitmask */ + +#define BAT_STAT_PRESENT 0x01 +#define BAT_STAT_FULL 0x02 +#define BAT_STAT_LOW 0x04 +#define BAT_STAT_DESTROY 0x08 +#define BAT_STAT_AC 0x10 +#define BAT_STAT_CHARGING 0x20 +#define BAT_STAT_DISCHARGING 0x40 + +#define BAT_ERR_INFOFAIL 0x02 +#define BAT_ERR_OVERVOLTAGE 0x04 +#define BAT_ERR_OVERTEMP 0x05 +#define BAT_ERR_GAUGESTOP 0x06 +#define BAT_ERR_OUT_OF_CONTROL 0x07 +#define BAT_ERR_ID_FAIL 0x09 +#define BAT_ERR_ACR_FAIL 0x10 + +#define BAT_ADDR_MFR_TYPE 0x5F + +/********************************************************************* + * Power + *********************************************************************/ + +static int olpc_ac_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + uint8_t status; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); + if (ret) + return ret; + + val->intval = !!(status & BAT_STAT_AC); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static enum power_supply_property olpc_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static struct power_supply olpc_ac = { + .name = "olpc-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = olpc_ac_props, + .num_properties = ARRAY_SIZE(olpc_ac_props), + .get_property = olpc_ac_get_prop, +}; + +/********************************************************************* + * Battery properties + *********************************************************************/ +static int olpc_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + int16_t ec_word; + uint8_t ec_byte; + + ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1); + if (ret) + return ret; + + /* Theoretically there's a race here -- the battery could be + removed immediately after we check whether it's present, and + then we query for some other property of the now-absent battery. + It doesn't matter though -- the EC will return the last-known + information, and it's as if we just ran that _little_ bit faster + and managed to read it out before the battery went away. */ + if (!(ec_byte & BAT_STAT_PRESENT) && psp != POWER_SUPPLY_PROP_PRESENT) + return -ENODEV; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (olpc_platform_info.ecver > 0x44) { + if (ec_byte & BAT_STAT_CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (ec_byte & BAT_STAT_DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (ec_byte & BAT_STAT_FULL) + val->intval = POWER_SUPPLY_STATUS_FULL; + else /* er,... */ + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + /* Older EC didn't report charge/discharge bits */ + if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */ + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (ec_byte & BAT_STAT_FULL) + val->intval = POWER_SUPPLY_STATUS_FULL; + else /* Not _necessarily_ true but EC doesn't tell all yet */ + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + } + case POWER_SUPPLY_PROP_PRESENT: + val->intval = !!(ec_byte & BAT_STAT_PRESENT); + break; + + case POWER_SUPPLY_PROP_HEALTH: + if (ec_byte & BAT_STAT_DESTROY) + val->intval = POWER_SUPPLY_HEALTH_DEAD; + else { + ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1); + if (ret) + return ret; + + switch (ec_byte) { + case 0: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + + case BAT_ERR_OVERTEMP: + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + break; + + case BAT_ERR_OVERVOLTAGE: + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + + case BAT_ERR_INFOFAIL: + case BAT_ERR_OUT_OF_CONTROL: + case BAT_ERR_ID_FAIL: + case BAT_ERR_ACR_FAIL: + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + + default: + /* Eep. We don't know this failure code */ + return -EIO; + } + } + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + ec_byte = BAT_ADDR_MFR_TYPE; + ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); + if (ret) + return ret; + + switch (ec_byte >> 4) { + case 1: + val->strval = "Gold Peak"; + break; + case 2: + val->strval = "BYD"; + break; + default: + val->strval = "Unknown"; + break; + } + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + ec_byte = BAT_ADDR_MFR_TYPE; + ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); + if (ret) + return ret; + + switch (ec_byte & 0xf) { + case 1: + val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; + break; + case 2: + val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe; + break; + default: + val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + break; + } + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2); + if (ret) + return ret; + + ec_word = be16_to_cpu(ec_word); + val->intval = ec_word * 9760L / 32; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); + if (ret) + return ret; + + ec_word = be16_to_cpu(ec_word); + val->intval = ec_word * 15625L / 120; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); + if (ret) + return ret; + val->intval = ec_byte; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + if (ec_byte & BAT_STAT_FULL) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (ec_byte & BAT_STAT_LOW) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + break; + case POWER_SUPPLY_PROP_TEMP: + ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); + if (ret) + return ret; + ec_word = be16_to_cpu(ec_word); + val->intval = ec_word * 100 / 256; + break; + case POWER_SUPPLY_PROP_TEMP_AMBIENT: + ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); + if (ret) + return ret; + + ec_word = be16_to_cpu(ec_word); + val->intval = ec_word * 100 / 256; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property olpc_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_AMBIENT, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +/********************************************************************* + * Initialisation + *********************************************************************/ + +static struct platform_device *bat_pdev; + +static struct power_supply olpc_bat = { + .properties = olpc_bat_props, + .num_properties = ARRAY_SIZE(olpc_bat_props), + .get_property = olpc_bat_get_property, + .use_for_apm = 1, +}; + +void olpc_battery_trigger_uevent(unsigned long cause) +{ + if (cause & EC_SCI_SRC_ACPWR) + kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE); + if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY)) + kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE); +} + +static int __init olpc_bat_init(void) +{ + int ret = 0; + uint8_t status; + + if (!olpc_platform_info.ecver) + return -ENXIO; + if (olpc_platform_info.ecver < 0x43) { + printk(KERN_NOTICE "OLPC EC version 0x%02x too old for battery driver.\n", olpc_platform_info.ecver); + return -ENXIO; + } + + ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); + if (ret) + return ret; + + /* Ignore the status. It doesn't actually matter */ + + bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0); + if (IS_ERR(bat_pdev)) + return PTR_ERR(bat_pdev); + + ret = power_supply_register(&bat_pdev->dev, &olpc_ac); + if (ret) + goto ac_failed; + + olpc_bat.name = bat_pdev->name; + + ret = power_supply_register(&bat_pdev->dev, &olpc_bat); + if (ret) + goto battery_failed; + + olpc_register_battery_callback(&olpc_battery_trigger_uevent); + goto success; + +battery_failed: + power_supply_unregister(&olpc_ac); +ac_failed: + platform_device_unregister(bat_pdev); +success: + return ret; +} + +static void __exit olpc_bat_exit(void) +{ + olpc_deregister_battery_callback(); + power_supply_unregister(&olpc_bat); + power_supply_unregister(&olpc_ac); + platform_device_unregister(bat_pdev); + return; +} + +module_init(olpc_bat_init); +module_exit(olpc_bat_exit); + +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine"); diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c new file mode 100644 index 00000000000..4e1eb040e14 --- /dev/null +++ b/drivers/power/pda_power.c @@ -0,0 +1,261 @@ +/* + * Common power driver for PDAs and phones with one or two external + * power supplies (AC/USB) connected to main and backup batteries, + * and optional builtin charger. + * + * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/power_supply.h> +#include <linux/pda_power.h> +#include <linux/timer.h> +#include <linux/jiffies.h> + +static inline unsigned int get_irq_flags(struct resource *res) +{ + unsigned int flags = IRQF_DISABLED | IRQF_SHARED; + + flags |= res->flags & IRQF_TRIGGER_MASK; + + return flags; +} + +static struct device *dev; +static struct pda_power_pdata *pdata; +static struct resource *ac_irq, *usb_irq; +static struct timer_list charger_timer; +static struct timer_list supply_timer; + +static int pda_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = pdata->is_ac_online ? + pdata->is_ac_online() : 0; + else + val->intval = pdata->is_usb_online ? + pdata->is_usb_online() : 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property pda_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *pda_power_supplied_to[] = { + "main-battery", + "backup-battery", +}; + +static struct power_supply pda_power_supplies[] = { + { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = pda_power_supplied_to, + .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), + .properties = pda_power_props, + .num_properties = ARRAY_SIZE(pda_power_props), + .get_property = pda_power_get_property, + }, + { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .supplied_to = pda_power_supplied_to, + .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), + .properties = pda_power_props, + .num_properties = ARRAY_SIZE(pda_power_props), + .get_property = pda_power_get_property, + }, +}; + +static void update_charger(void) +{ + if (!pdata->set_charge) + return; + + if (pdata->is_ac_online && pdata->is_ac_online()) { + dev_dbg(dev, "charger on (AC)\n"); + pdata->set_charge(PDA_POWER_CHARGE_AC); + } else if (pdata->is_usb_online && pdata->is_usb_online()) { + dev_dbg(dev, "charger on (USB)\n"); + pdata->set_charge(PDA_POWER_CHARGE_USB); + } else { + dev_dbg(dev, "charger off\n"); + pdata->set_charge(0); + } + + return; +} + +static void supply_timer_func(unsigned long irq) +{ + if (ac_irq && irq == ac_irq->start) + power_supply_changed(&pda_power_supplies[0]); + else if (usb_irq && irq == usb_irq->start) + power_supply_changed(&pda_power_supplies[1]); + return; +} + +static void charger_timer_func(unsigned long irq) +{ + update_charger(); + + /* Okay, charger set. Now wait a bit before notifying supplicants, + * charge power should stabilize. */ + supply_timer.data = irq; + mod_timer(&supply_timer, + jiffies + msecs_to_jiffies(pdata->wait_for_charger)); + return; +} + +static irqreturn_t power_changed_isr(int irq, void *unused) +{ + /* Wait a bit before reading ac/usb line status and setting charger, + * because ac/usb status readings may lag from irq. */ + charger_timer.data = irq; + mod_timer(&charger_timer, + jiffies + msecs_to_jiffies(pdata->wait_for_status)); + return IRQ_HANDLED; +} + +static int pda_power_probe(struct platform_device *pdev) +{ + int ret = 0; + + dev = &pdev->dev; + + if (pdev->id != -1) { + dev_err(dev, "it's meaningless to register several " + "pda_powers; use id = -1\n"); + ret = -EINVAL; + goto wrongid; + } + + pdata = pdev->dev.platform_data; + + update_charger(); + + if (!pdata->wait_for_status) + pdata->wait_for_status = 500; + + if (!pdata->wait_for_charger) + pdata->wait_for_charger = 500; + + setup_timer(&charger_timer, charger_timer_func, 0); + setup_timer(&supply_timer, supply_timer_func, 0); + + ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); + usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); + if (!ac_irq && !usb_irq) { + dev_err(dev, "no ac/usb irq specified\n"); + ret = -ENODEV; + goto noirqs; + } + + if (pdata->supplied_to) { + pda_power_supplies[0].supplied_to = pdata->supplied_to; + pda_power_supplies[1].supplied_to = pdata->supplied_to; + pda_power_supplies[0].num_supplicants = pdata->num_supplicants; + pda_power_supplies[1].num_supplicants = pdata->num_supplicants; + } + + ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); + if (ret) { + dev_err(dev, "failed to register %s power supply\n", + pda_power_supplies[0].name); + goto supply0_failed; + } + + ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]); + if (ret) { + dev_err(dev, "failed to register %s power supply\n", + pda_power_supplies[1].name); + goto supply1_failed; + } + + if (ac_irq) { + ret = request_irq(ac_irq->start, power_changed_isr, + get_irq_flags(ac_irq), ac_irq->name, + &pda_power_supplies[0]); + if (ret) { + dev_err(dev, "request ac irq failed\n"); + goto ac_irq_failed; + } + } + + if (usb_irq) { + ret = request_irq(usb_irq->start, power_changed_isr, + get_irq_flags(usb_irq), usb_irq->name, + &pda_power_supplies[1]); + if (ret) { + dev_err(dev, "request usb irq failed\n"); + goto usb_irq_failed; + } + } + + goto success; + +usb_irq_failed: + if (ac_irq) + free_irq(ac_irq->start, &pda_power_supplies[0]); +ac_irq_failed: + power_supply_unregister(&pda_power_supplies[1]); +supply1_failed: + power_supply_unregister(&pda_power_supplies[0]); +supply0_failed: +noirqs: +wrongid: +success: + return ret; +} + +static int pda_power_remove(struct platform_device *pdev) +{ + if (usb_irq) + free_irq(usb_irq->start, &pda_power_supplies[1]); + if (ac_irq) + free_irq(ac_irq->start, &pda_power_supplies[0]); + del_timer_sync(&charger_timer); + del_timer_sync(&supply_timer); + power_supply_unregister(&pda_power_supplies[1]); + power_supply_unregister(&pda_power_supplies[0]); + return 0; +} + +static struct platform_driver pda_power_pdrv = { + .driver = { + .name = "pda-power", + }, + .probe = pda_power_probe, + .remove = pda_power_remove, +}; + +static int __init pda_power_init(void) +{ + return platform_driver_register(&pda_power_pdrv); +} + +static void __exit pda_power_exit(void) +{ + platform_driver_unregister(&pda_power_pdrv); + return; +} + +module_init(pda_power_init); +module_exit(pda_power_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); diff --git a/drivers/power/pmu_battery.c b/drivers/power/pmu_battery.c new file mode 100644 index 00000000000..2fea4af0e40 --- /dev/null +++ b/drivers/power/pmu_battery.c @@ -0,0 +1,215 @@ +/* + * Battery class driver for Apple PMU + * + * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/power_supply.h> +#include <linux/adb.h> +#include <linux/pmu.h> + +static struct pmu_battery_dev { + struct power_supply bat; + struct pmu_battery_info *pbi; + char name[16]; + int propval; +} *pbats[PMU_MAX_BATTERIES]; + +#define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat) + +/********************************************************************* + * Power + *********************************************************************/ + +static int pmu_get_ac_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) || + (pmu_battery_count == 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property pmu_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static struct power_supply pmu_ac = { + .name = "pmu-ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = pmu_ac_props, + .num_properties = ARRAY_SIZE(pmu_ac_props), + .get_property = pmu_get_ac_prop, +}; + +/********************************************************************* + * Battery properties + *********************************************************************/ + +static char *pmu_batt_types[] = { + "Smart", "Comet", "Hooper", "Unknown" +}; + +static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi) +{ + switch (pbi->flags & PMU_BATT_TYPE_MASK) { + case PMU_BATT_TYPE_SMART: + return pmu_batt_types[0]; + case PMU_BATT_TYPE_COMET: + return pmu_batt_types[1]; + case PMU_BATT_TYPE_HOOPER: + return pmu_batt_types[2]; + default: break; + } + return pmu_batt_types[3]; +} + +static int pmu_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy); + struct pmu_battery_info *pbi = pbat->pbi; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (pbi->flags & PMU_BATT_CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = !!(pbi->flags & PMU_BATT_PRESENT); + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = pmu_bat_get_model_name(pbi); + break; + case POWER_SUPPLY_PROP_ENERGY_AVG: + val->intval = pbi->charge * 1000; /* mWh -> µWh */ + break; + case POWER_SUPPLY_PROP_ENERGY_FULL: + val->intval = pbi->max_charge * 1000; /* mWh -> µWh */ + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = pbi->amperage * 1000; /* mA -> µA */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + val->intval = pbi->voltage * 1000; /* mV -> µV */ + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + val->intval = pbi->time_remaining; + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property pmu_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_ENERGY_AVG, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, +}; + +/********************************************************************* + * Initialisation + *********************************************************************/ + +static struct platform_device *bat_pdev; + +static int __init pmu_bat_init(void) +{ + int ret; + int i; + + bat_pdev = platform_device_register_simple("pmu-battery", + 0, NULL, 0); + if (IS_ERR(bat_pdev)) { + ret = PTR_ERR(bat_pdev); + goto pdev_register_failed; + } + + ret = power_supply_register(&bat_pdev->dev, &pmu_ac); + if (ret) + goto ac_register_failed; + + for (i = 0; i < pmu_battery_count; i++) { + struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat), + GFP_KERNEL); + if (!pbat) + break; + + sprintf(pbat->name, "PMU battery %d", i); + pbat->bat.name = pbat->name; + pbat->bat.properties = pmu_bat_props; + pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props); + pbat->bat.get_property = pmu_bat_get_property; + pbat->pbi = &pmu_batteries[i]; + + ret = power_supply_register(&bat_pdev->dev, &pbat->bat); + if (ret) { + kfree(pbat); + goto battery_register_failed; + } + pbats[i] = pbat; + } + + goto success; + +battery_register_failed: + while (i--) { + if (!pbats[i]) + continue; + power_supply_unregister(&pbats[i]->bat); + kfree(pbats[i]); + } + power_supply_unregister(&pmu_ac); +ac_register_failed: + platform_device_unregister(bat_pdev); +pdev_register_failed: +success: + return ret; +} + +static void __exit pmu_bat_exit(void) +{ + int i; + + for (i = 0; i < PMU_MAX_BATTERIES; i++) { + if (!pbats[i]) + continue; + power_supply_unregister(&pbats[i]->bat); + kfree(pbats[i]); + } + power_supply_unregister(&pmu_ac); + platform_device_unregister(bat_pdev); + + return; +} + +module_init(pmu_bat_init); +module_exit(pmu_bat_exit); + +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PMU battery driver"); diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h new file mode 100644 index 00000000000..a9880d468ee --- /dev/null +++ b/drivers/power/power_supply.h @@ -0,0 +1,42 @@ +/* + * Functions private to power supply class + * + * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> + * Copyright © 2004 Szabolcs Gyurko + * Copyright © 2003 Ian Molton <spyro@f2s.com> + * + * Modified: 2004, Oct Szabolcs Gyurko + * + * You may use this code as per GPL version 2 + */ + +#ifdef CONFIG_SYSFS + +extern int power_supply_create_attrs(struct power_supply *psy); +extern void power_supply_remove_attrs(struct power_supply *psy); +extern int power_supply_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size); + +#else + +static inline int power_supply_create_attrs(struct power_supply *psy) +{ return 0; } +static inline void power_supply_remove_attrs(struct power_supply *psy) {} +#define power_supply_uevent NULL + +#endif /* CONFIG_SYSFS */ + +#ifdef CONFIG_LEDS_TRIGGERS + +extern void power_supply_update_leds(struct power_supply *psy); +extern int power_supply_create_triggers(struct power_supply *psy); +extern void power_supply_remove_triggers(struct power_supply *psy); + +#else + +static inline void power_supply_update_leds(struct power_supply *psy) {} +static inline int power_supply_create_triggers(struct power_supply *psy) +{ return 0; } +static inline void power_supply_remove_triggers(struct power_supply *psy) {} + +#endif /* CONFIG_LEDS_TRIGGERS */ diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c new file mode 100644 index 00000000000..e87ea515675 --- /dev/null +++ b/drivers/power/power_supply_core.c @@ -0,0 +1,168 @@ +/* + * Universal power supply monitor class + * + * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> + * Copyright © 2004 Szabolcs Gyurko + * Copyright © 2003 Ian Molton <spyro@f2s.com> + * + * Modified: 2004, Oct Szabolcs Gyurko + * + * You may use this code as per GPL version 2 + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/power_supply.h> +#include "power_supply.h" + +struct class *power_supply_class; + +static void power_supply_changed_work(struct work_struct *work) +{ + struct power_supply *psy = container_of(work, struct power_supply, + changed_work); + int i; + + dev_dbg(psy->dev, "%s\n", __FUNCTION__); + + for (i = 0; i < psy->num_supplicants; i++) { + struct device *dev; + + down(&power_supply_class->sem); + list_for_each_entry(dev, &power_supply_class->devices, node) { + struct power_supply *pst = dev_get_drvdata(dev); + + if (!strcmp(psy->supplied_to[i], pst->name)) { + if (pst->external_power_changed) + pst->external_power_changed(pst); + } + } + up(&power_supply_class->sem); + } + + power_supply_update_leds(psy); + + kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); + + return; +} + +void power_supply_changed(struct power_supply *psy) +{ + dev_dbg(psy->dev, "%s\n", __FUNCTION__); + + schedule_work(&psy->changed_work); + + return; +} + +int power_supply_am_i_supplied(struct power_supply *psy) +{ + union power_supply_propval ret = {0,}; + struct device *dev; + + down(&power_supply_class->sem); + list_for_each_entry(dev, &power_supply_class->devices, node) { + struct power_supply *epsy = dev_get_drvdata(dev); + int i; + + for (i = 0; i < epsy->num_supplicants; i++) { + if (!strcmp(epsy->supplied_to[i], psy->name)) { + if (epsy->get_property(epsy, + POWER_SUPPLY_PROP_ONLINE, &ret)) + continue; + if (ret.intval) + goto out; + } + } + } +out: + up(&power_supply_class->sem); + + dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval); + + return ret.intval; +} + +int power_supply_register(struct device *parent, struct power_supply *psy) +{ + int rc = 0; + + psy->dev = device_create(power_supply_class, parent, 0, + "%s", psy->name); + if (IS_ERR(psy->dev)) { + rc = PTR_ERR(psy->dev); + goto dev_create_failed; + } + + dev_set_drvdata(psy->dev, psy); + + INIT_WORK(&psy->changed_work, power_supply_changed_work); + + rc = power_supply_create_attrs(psy); + if (rc) + goto create_attrs_failed; + + rc = power_supply_create_triggers(psy); + if (rc) + goto create_triggers_failed; + + power_supply_changed(psy); + + goto success; + +create_triggers_failed: + power_supply_remove_attrs(psy); +create_attrs_failed: + device_unregister(psy->dev); +dev_create_failed: +success: + return rc; +} + +void power_supply_unregister(struct power_supply *psy) +{ + flush_scheduled_work(); + power_supply_remove_triggers(psy); + power_supply_remove_attrs(psy); + device_unregister(psy->dev); + return; +} + +static int __init power_supply_class_init(void) +{ + power_supply_class = class_create(THIS_MODULE, "power_supply"); + + if (IS_ERR(power_supply_class)) + return PTR_ERR(power_supply_class); + + power_supply_class->dev_uevent = power_supply_uevent; + + return 0; +} + +static void __exit power_supply_class_exit(void) +{ + class_destroy(power_supply_class); + return; +} + +EXPORT_SYMBOL_GPL(power_supply_changed); +EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); +EXPORT_SYMBOL_GPL(power_supply_register); +EXPORT_SYMBOL_GPL(power_supply_unregister); + +/* exported for the APM Power driver, APM emulation */ +EXPORT_SYMBOL_GPL(power_supply_class); + +subsys_initcall(power_supply_class_init); +module_exit(power_supply_class_exit); + +MODULE_DESCRIPTION("Universal power supply monitor class"); +MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, " + "Szabolcs Gyurko, " + "Anton Vorontsov <cbou@mail.ru>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c new file mode 100644 index 00000000000..7232490bb59 --- /dev/null +++ b/drivers/power/power_supply_leds.c @@ -0,0 +1,176 @@ +/* + * LEDs triggers for power supply class + * + * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> + * Copyright © 2004 Szabolcs Gyurko + * Copyright © 2003 Ian Molton <spyro@f2s.com> + * + * Modified: 2004, Oct Szabolcs Gyurko + * + * You may use this code as per GPL version 2 + */ + +#include <linux/power_supply.h> + +/* Battery specific LEDs triggers. */ + +static void power_supply_update_bat_leds(struct power_supply *psy) +{ + union power_supply_propval status; + + if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) + return; + + dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, status.intval); + + switch (status.intval) { + case POWER_SUPPLY_STATUS_FULL: + led_trigger_event(psy->charging_full_trig, LED_FULL); + led_trigger_event(psy->charging_trig, LED_OFF); + led_trigger_event(psy->full_trig, LED_FULL); + break; + case POWER_SUPPLY_STATUS_CHARGING: + led_trigger_event(psy->charging_full_trig, LED_FULL); + led_trigger_event(psy->charging_trig, LED_FULL); + led_trigger_event(psy->full_trig, LED_OFF); + break; + default: + led_trigger_event(psy->charging_full_trig, LED_OFF); + led_trigger_event(psy->charging_trig, LED_OFF); + led_trigger_event(psy->full_trig, LED_OFF); + break; + } + + return; +} + +static int power_supply_create_bat_triggers(struct power_supply *psy) +{ + int rc = 0; + + psy->charging_full_trig_name = kmalloc(strlen(psy->name) + + sizeof("-charging-or-full"), GFP_KERNEL); + if (!psy->charging_full_trig_name) + goto charging_full_failed; + + psy->charging_trig_name = kmalloc(strlen(psy->name) + + sizeof("-charging"), GFP_KERNEL); + if (!psy->charging_trig_name) + goto charging_failed; + + psy->full_trig_name = kmalloc(strlen(psy->name) + + sizeof("-full"), GFP_KERNEL); + if (!psy->full_trig_name) + goto full_failed; + + strcpy(psy->charging_full_trig_name, psy->name); + strcat(psy->charging_full_trig_name, "-charging-or-full"); + strcpy(psy->charging_trig_name, psy->name); + strcat(psy->charging_trig_name, "-charging"); + strcpy(psy->full_trig_name, psy->name); + strcat(psy->full_trig_name, "-full"); + + led_trigger_register_simple(psy->charging_full_trig_name, + &psy->charging_full_trig); + led_trigger_register_simple(psy->charging_trig_name, + &psy->charging_trig); + led_trigger_register_simple(psy->full_trig_name, + &psy->full_trig); + + goto success; + +full_failed: + kfree(psy->charging_trig_name); +charging_failed: + kfree(psy->charging_full_trig_name); +charging_full_failed: + rc = -ENOMEM; +success: + return rc; +} + +static void power_supply_remove_bat_triggers(struct power_supply *psy) +{ + led_trigger_unregister_simple(psy->charging_full_trig); + led_trigger_unregister_simple(psy->charging_trig); + led_trigger_unregister_simple(psy->full_trig); + kfree(psy->full_trig_name); + kfree(psy->charging_trig_name); + kfree(psy->charging_full_trig_name); + return; +} + +/* Generated power specific LEDs triggers. */ + +static void power_supply_update_gen_leds(struct power_supply *psy) +{ + union power_supply_propval online; + + if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online)) + return; + + dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, online.intval); + + if (online.intval) + led_trigger_event(psy->online_trig, LED_FULL); + else + led_trigger_event(psy->online_trig, LED_OFF); + + return; +} + +static int power_supply_create_gen_triggers(struct power_supply *psy) +{ + int rc = 0; + + psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"), + GFP_KERNEL); + if (!psy->online_trig_name) + goto online_failed; + + strcpy(psy->online_trig_name, psy->name); + strcat(psy->online_trig_name, "-online"); + + led_trigger_register_simple(psy->online_trig_name, &psy->online_trig); + + goto success; + +online_failed: + rc = -ENOMEM; +success: + return rc; +} + +static void power_supply_remove_gen_triggers(struct power_supply *psy) +{ + led_trigger_unregister_simple(psy->online_trig); + kfree(psy->online_trig_name); + return; +} + +/* Choice what triggers to create&update. */ + +void power_supply_update_leds(struct power_supply *psy) +{ + if (psy->type == POWER_SUPPLY_TYPE_BATTERY) + power_supply_update_bat_leds(psy); + else + power_supply_update_gen_leds(psy); + return; +} + +int power_supply_create_triggers(struct power_supply *psy) +{ + if (psy->type == POWER_SUPPLY_TYPE_BATTERY) + return power_supply_create_bat_triggers(psy); + return power_supply_create_gen_triggers(psy); +} + +void power_supply_remove_triggers(struct power_supply *psy) +{ + if (psy->type == POWER_SUPPLY_TYPE_BATTERY) + power_supply_remove_bat_triggers(psy); + else + power_supply_remove_gen_triggers(psy); + return; +} diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c new file mode 100644 index 00000000000..c07d4258d34 --- /dev/null +++ b/drivers/power/power_supply_sysfs.c @@ -0,0 +1,299 @@ +/* + * Sysfs interface for the universal power supply monitor class + * + * Copyright © 2007 David Woodhouse <dwmw2@infradead.org> + * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> + * Copyright © 2004 Szabolcs Gyurko + * Copyright © 2003 Ian Molton <spyro@f2s.com> + * + * Modified: 2004, Oct Szabolcs Gyurko + * + * You may use this code as per GPL version 2 + */ + +#include <linux/ctype.h> +#include <linux/power_supply.h> + +/* + * This is because the name "current" breaks the device attr macro. + * The "current" word resolves to "(get_current())" so instead of + * "current" "(get_current())" appears in the sysfs. + * + * The source of this definition is the device.h which calls __ATTR + * macro in sysfs.h which calls the __stringify macro. + * + * Only modification that the name is not tried to be resolved + * (as a macro let's say). + */ + +#define POWER_SUPPLY_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \ + .show = power_supply_show_property, \ + .store = NULL, \ +} + +static struct device_attribute power_supply_attrs[]; + +static ssize_t power_supply_show_property(struct device *dev, + struct device_attribute *attr, + char *buf) { + static char *status_text[] = { + "Unknown", "Charging", "Discharging", "Not charging", "Full" + }; + static char *health_text[] = { + "Unknown", "Good", "Overheat", "Dead", "Over voltage", + "Unspecified failure" + }; + static char *technology_text[] = { + "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd" + }; + static char *capacity_level_text[] = { + "Unknown", "Critical", "Low", "Normal", "High", "Full" + }; + ssize_t ret; + struct power_supply *psy = dev_get_drvdata(dev); + const ptrdiff_t off = attr - power_supply_attrs; + union power_supply_propval value; + + ret = psy->get_property(psy, off, &value); + + if (ret < 0) { + if (ret != -ENODEV) + dev_err(dev, "driver failed to report `%s' property\n", + attr->attr.name); + return ret; + } + + if (off == POWER_SUPPLY_PROP_STATUS) + return sprintf(buf, "%s\n", status_text[value.intval]); + else if (off == POWER_SUPPLY_PROP_HEALTH) + return sprintf(buf, "%s\n", health_text[value.intval]); + else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) + return sprintf(buf, "%s\n", technology_text[value.intval]); + else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) + return sprintf(buf, "%s\n", + capacity_level_text[value.intval]); + else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) + return sprintf(buf, "%s\n", value.strval); + + return sprintf(buf, "%d\n", value.intval); +} + +/* Must be in the same order as POWER_SUPPLY_PROP_* */ +static struct device_attribute power_supply_attrs[] = { + /* Properties of type `int' */ + POWER_SUPPLY_ATTR(status), + POWER_SUPPLY_ATTR(health), + POWER_SUPPLY_ATTR(present), + POWER_SUPPLY_ATTR(online), + POWER_SUPPLY_ATTR(technology), + POWER_SUPPLY_ATTR(voltage_max_design), + POWER_SUPPLY_ATTR(voltage_min_design), + POWER_SUPPLY_ATTR(voltage_now), + POWER_SUPPLY_ATTR(voltage_avg), + POWER_SUPPLY_ATTR(current_now), + POWER_SUPPLY_ATTR(current_avg), + POWER_SUPPLY_ATTR(charge_full_design), + POWER_SUPPLY_ATTR(charge_empty_design), + POWER_SUPPLY_ATTR(charge_full), + POWER_SUPPLY_ATTR(charge_empty), + POWER_SUPPLY_ATTR(charge_now), + POWER_SUPPLY_ATTR(charge_avg), + POWER_SUPPLY_ATTR(energy_full_design), + POWER_SUPPLY_ATTR(energy_empty_design), + POWER_SUPPLY_ATTR(energy_full), + POWER_SUPPLY_ATTR(energy_empty), + POWER_SUPPLY_ATTR(energy_now), + POWER_SUPPLY_ATTR(energy_avg), + POWER_SUPPLY_ATTR(capacity), + POWER_SUPPLY_ATTR(capacity_level), + POWER_SUPPLY_ATTR(temp), + POWER_SUPPLY_ATTR(temp_ambient), + POWER_SUPPLY_ATTR(time_to_empty_now), + POWER_SUPPLY_ATTR(time_to_empty_avg), + POWER_SUPPLY_ATTR(time_to_full_now), + POWER_SUPPLY_ATTR(time_to_full_avg), + /* Properties of type `const char *' */ + POWER_SUPPLY_ATTR(model_name), + POWER_SUPPLY_ATTR(manufacturer), +}; + +static ssize_t power_supply_show_static_attrs(struct device *dev, + struct device_attribute *attr, + char *buf) { + static char *type_text[] = { "Battery", "UPS", "Mains", "USB" }; + struct power_supply *psy = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", type_text[psy->type]); +} + +static struct device_attribute power_supply_static_attrs[] = { + __ATTR(type, 0444, power_supply_show_static_attrs, NULL), +}; + +int power_supply_create_attrs(struct power_supply *psy) +{ + int rc = 0; + int i, j; + + for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) { + rc = device_create_file(psy->dev, + &power_supply_static_attrs[i]); + if (rc) + goto statics_failed; + } + + for (j = 0; j < psy->num_properties; j++) { + rc = device_create_file(psy->dev, + &power_supply_attrs[psy->properties[j]]); + if (rc) + goto dynamics_failed; + } + + goto succeed; + +dynamics_failed: + while (j--) + device_remove_file(psy->dev, + &power_supply_attrs[psy->properties[j]]); +statics_failed: + while (i--) + device_remove_file(psy->dev, + &power_supply_static_attrs[psy->properties[i]]); +succeed: + return rc; +} + +void power_supply_remove_attrs(struct power_supply *psy) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) + device_remove_file(psy->dev, + &power_supply_static_attrs[i]); + + for (i = 0; i < psy->num_properties; i++) + device_remove_file(psy->dev, + &power_supply_attrs[psy->properties[i]]); + + return; +} + +static char *kstruprdup(const char *str, gfp_t gfp) +{ + char *ret, *ustr; + + ustr = ret = kmalloc(strlen(str) + 1, gfp); + + if (!ret) + return NULL; + + while (*str) + *ustr++ = toupper(*str++); + + *ustr = 0; + + return ret; +} + +int power_supply_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct power_supply *psy = dev_get_drvdata(dev); + int i = 0, length = 0, ret = 0, j; + char *prop_buf; + char *attrname; + + dev_dbg(dev, "uevent\n"); + + if (!psy) { + dev_dbg(dev, "No power supply yet\n"); + return ret; + } + + dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name); + + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, + &length, "POWER_SUPPLY_NAME=%s", psy->name); + if (ret) + return ret; + + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!prop_buf) + return -ENOMEM; + + for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) { + struct device_attribute *attr; + char *line; + + attr = &power_supply_static_attrs[j]; + + ret = power_supply_show_static_attrs(dev, attr, prop_buf); + if (ret < 0) + goto out; + + line = strchr(prop_buf, '\n'); + if (line) + *line = 0; + + attrname = kstruprdup(attr->attr.name, GFP_KERNEL); + if (!attrname) { + ret = -ENOMEM; + goto out; + } + + dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf); + + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, + &length, "POWER_SUPPLY_%s=%s", + attrname, prop_buf); + kfree(attrname); + if (ret) + goto out; + } + + dev_dbg(dev, "%zd dynamic props\n", psy->num_properties); + + for (j = 0; j < psy->num_properties; j++) { + struct device_attribute *attr; + char *line; + + attr = &power_supply_attrs[psy->properties[j]]; + + ret = power_supply_show_property(dev, attr, prop_buf); + if (ret == -ENODEV) { + /* When a battery is absent, we expect -ENODEV. Don't abort; + send the uevent with at least the the PRESENT=0 property */ + ret = 0; + continue; + } + + if (ret < 0) + goto out; + + line = strchr(prop_buf, '\n'); + if (line) + *line = 0; + + attrname = kstruprdup(attr->attr.name, GFP_KERNEL); + if (!attrname) { + ret = -ENOMEM; + goto out; + } + + dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); + + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, + &length, "POWER_SUPPLY_%s=%s", + attrname, prop_buf); + kfree(attrname); + if (ret) + goto out; + } + +out: + free_page((unsigned long)prop_buf); + + return ret; +} diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 8b3b0f4a157..ac7e8ef504c 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -28,6 +28,7 @@ static struct proc_dir_entry *dasd_proc_root_entry = NULL; static struct proc_dir_entry *dasd_devices_entry = NULL; static struct proc_dir_entry *dasd_statistics_entry = NULL; +#ifdef CONFIG_DASD_PROFILE static char * dasd_get_user_string(const char __user *user_buf, size_t user_len) { @@ -47,6 +48,7 @@ dasd_get_user_string(const char __user *user_buf, size_t user_len) buffer[user_len] = 0; return buffer; } +#endif /* CONFIG_DASD_PROFILE */ static int dasd_devices_show(struct seq_file *m, void *v) @@ -167,6 +169,7 @@ dasd_calc_metrics(char *page, char **start, off_t off, return len; } +#ifdef CONFIG_DASD_PROFILE static char * dasd_statistics_array(char *str, unsigned int *array, int shift) { @@ -180,6 +183,7 @@ dasd_statistics_array(char *str, unsigned int *array, int shift) str += sprintf(str,"\n"); return str; } +#endif /* CONFIG_DASD_PROFILE */ static int dasd_statistics_read(char *page, char **start, off_t off, diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index dbb99d1b6f5..c7318a12585 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -72,6 +72,18 @@ typedef unsigned int sclp_cmdw_t; typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ +struct sccb_header { + u16 length; + u8 function_code; + u8 control_mask[3]; + u16 response_code; +} __attribute__((packed)); + +extern u64 sclp_facilities; + +#define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL) +#define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) + struct gds_subvector { u8 length; u8 key; diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c index a66b914519b..c68f5e7e63a 100644 --- a/drivers/s390/char/sclp_chp.c +++ b/drivers/s390/char/sclp_chp.c @@ -55,6 +55,8 @@ static int do_configure(sclp_cmdw_t cmd) struct chp_cfg_data *data; int rc; + if (!SCLP_HAS_CHP_RECONFIG) + return -EOPNOTSUPP; /* Prepare sccb. */ data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!data) @@ -152,6 +154,8 @@ int sclp_chp_read_info(struct sclp_chp_info *info) struct chp_info_data *data; int rc; + if (!SCLP_HAS_CHP_INFO) + return -EOPNOTSUPP; /* Prepare sccb. */ data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!data) diff --git a/drivers/s390/char/sclp_info.c b/drivers/s390/char/sclp_info.c index 7bcbe643b08..a1136e05275 100644 --- a/drivers/s390/char/sclp_info.c +++ b/drivers/s390/char/sclp_info.c @@ -11,47 +11,106 @@ #include <asm/sclp.h> #include "sclp.h" -struct sclp_readinfo_sccb s390_readinfo_sccb; +struct sclp_readinfo_sccb { + struct sccb_header header; /* 0-7 */ + u16 rnmax; /* 8-9 */ + u8 rnsize; /* 10 */ + u8 _reserved0[24 - 11]; /* 11-23 */ + u8 loadparm[8]; /* 24-31 */ + u8 _reserved1[48 - 32]; /* 32-47 */ + u64 facilities; /* 48-55 */ + u8 _reserved2[91 - 56]; /* 56-90 */ + u8 flags; /* 91 */ + u8 _reserved3[100 - 92]; /* 92-99 */ + u32 rnsize2; /* 100-103 */ + u64 rnmax2; /* 104-111 */ + u8 _reserved4[4096 - 112]; /* 112-4095 */ +} __attribute__((packed, aligned(4096))); + +static struct sclp_readinfo_sccb __initdata early_readinfo_sccb; +static int __initdata early_readinfo_sccb_valid; + +u64 sclp_facilities; void __init sclp_readinfo_early(void) { - sclp_cmdw_t command; - struct sccb_header *sccb; int ret; + int i; + struct sclp_readinfo_sccb *sccb; + sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, + SCLP_CMDW_READ_SCP_INFO}; - __ctl_set_bit(0, 9); /* enable service signal subclass mask */ - - sccb = &s390_readinfo_sccb.header; - command = SCLP_CMDW_READ_SCP_INFO_FORCED; - while (1) { - u16 response; - - memset(&s390_readinfo_sccb, 0, sizeof(s390_readinfo_sccb)); - sccb->length = sizeof(s390_readinfo_sccb); - sccb->control_mask[2] = 0x80; - - ret = sclp_service_call(command, &s390_readinfo_sccb); - - if (ret == -EIO) - goto out; - if (ret == -EBUSY) - continue; + /* Enable service signal subclass mask. */ + __ctl_set_bit(0, 9); + sccb = &early_readinfo_sccb; + for (i = 0; i < ARRAY_SIZE(commands); i++) { + do { + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->header.control_mask[2] = 0x80; + ret = sclp_service_call(commands[i], sccb); + } while (ret == -EBUSY); + if (ret) + break; __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT | PSW_MASK_WAIT | PSW_DEFAULT_KEY); local_irq_disable(); + /* + * Contents of the sccb might have changed + * therefore a barrier is needed. + */ barrier(); + if (sccb->header.response_code == 0x10) { + early_readinfo_sccb_valid = 1; + break; + } + if (sccb->header.response_code != 0x1f0) + break; + } + /* Disable service signal subclass mask again. */ + __ctl_clear_bit(0, 9); +} - response = sccb->response_code; +void __init sclp_facilities_detect(void) +{ + if (!early_readinfo_sccb_valid) + return; + sclp_facilities = early_readinfo_sccb.facilities; +} - if (response == 0x10) - break; +unsigned long long __init sclp_memory_detect(void) +{ + unsigned long long memsize; + struct sclp_readinfo_sccb *sccb; - if (response != 0x1f0 || command == SCLP_CMDW_READ_SCP_INFO) - break; + if (!early_readinfo_sccb_valid) + return 0; + sccb = &early_readinfo_sccb; + if (sccb->rnsize) + memsize = sccb->rnsize << 20; + else + memsize = sccb->rnsize2 << 20; + if (sccb->rnmax) + memsize *= sccb->rnmax; + else + memsize *= sccb->rnmax2; + return memsize; +} - command = SCLP_CMDW_READ_SCP_INFO; - } -out: - __ctl_clear_bit(0, 9); /* disable service signal subclass mask */ +/* + * This function will be called after sclp_memory_detect(), which gets called + * early from early.c code. Therefore the sccb should have valid contents. + */ +void __init sclp_get_ipl_info(struct sclp_ipl_info *info) +{ + struct sclp_readinfo_sccb *sccb; + + if (!early_readinfo_sccb_valid) + return; + sccb = &early_readinfo_sccb; + info->is_valid = 1; + if (sccb->flags & 0x2) + info->has_dump = 1; + memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN); } diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index fce3dac5cb3..82e6a6b253e 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c @@ -175,13 +175,12 @@ static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static const struct file_operations vmcp_fops = { .owner = THIS_MODULE, - .open = &vmcp_open, - .release = &vmcp_release, - .read = &vmcp_read, - .llseek = &no_llseek, - .write = &vmcp_write, - .unlocked_ioctl = &vmcp_ioctl, - .compat_ioctl = &vmcp_ioctl + .open = vmcp_open, + .release = vmcp_release, + .read = vmcp_read, + .write = vmcp_write, + .unlocked_ioctl = vmcp_ioctl, + .compat_ioctl = vmcp_ioctl }; static struct miscdevice vmcp_dev = { diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index a5a00e9ae4d..12f7a4ce82c 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -835,7 +835,7 @@ static void vmlogrdr_cleanup(void) } -static int vmlogrdr_init(void) +static int __init vmlogrdr_init(void) { int rc; int i; @@ -885,7 +885,7 @@ cleanup: } -static void vmlogrdr_exit(void) +static void __exit vmlogrdr_exit(void) { vmlogrdr_cleanup(); printk (KERN_INFO "vmlogrdr: driver unloaded\n"); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 4e711a985d5..3712ede1672 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -156,7 +156,7 @@ static int memcpy_real(void *dest, unsigned long src, size_t count) return rc; } -static int memcpy_real_user(__user void *dest, unsigned long src, size_t count) +static int memcpy_real_user(void __user *dest, unsigned long src, size_t count) { static char buf[4096]; int offs = 0, size; diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 997f4687453..60b9347f7c9 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -27,7 +27,6 @@ /* * diag210 is used under VM to get information about a virtual device */ -#ifdef CONFIG_64BIT int diag210(struct diag210 * addr) { @@ -43,6 +42,7 @@ diag210(struct diag210 * addr) spin_lock_irqsave(&diag210_lock, flags); diag210_tmp = *addr; +#ifdef CONFIG_64BIT asm volatile( " lhi %0,-1\n" " sam31\n" @@ -51,19 +51,8 @@ diag210(struct diag210 * addr) " srl %0,28\n" "1: sam64\n" EX_TABLE(0b,1b) - : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory"); - - *addr = diag210_tmp; - spin_unlock_irqrestore(&diag210_lock, flags); - - return ccode; -} + : "=&d" (ccode) : "a" (&diag210_tmp) : "cc", "memory"); #else -int -diag210(struct diag210 * addr) -{ - int ccode; - asm volatile( " lhi %0,-1\n" " diag %1,0,0x210\n" @@ -71,11 +60,14 @@ diag210(struct diag210 * addr) " srl %0,28\n" "1:\n" EX_TABLE(0b,1b) - : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory"); + : "=&d" (ccode) : "a" (&diag210_tmp) : "cc", "memory"); +#endif + + *addr = diag210_tmp; + spin_unlock_irqrestore(&diag210_lock, flags); return ccode; } -#endif /* * Input : diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 5aac0ec3636..90bd2201451 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -43,6 +43,7 @@ static void ap_poll_all(unsigned long); static void ap_poll_timeout(unsigned long); static int ap_poll_thread_start(void); static void ap_poll_thread_stop(void); +static void ap_request_timeout(unsigned long); /** * Module description. @@ -189,6 +190,7 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) case AP_RESPONSE_NORMAL: return 0; case AP_RESPONSE_Q_FULL: + case AP_RESPONSE_RESET_IN_PROGRESS: return -EBUSY; default: /* Device is gone. */ return -ENODEV; @@ -252,6 +254,8 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) if (status.queue_empty) return -ENOENT; return -EBUSY; + case AP_RESPONSE_RESET_IN_PROGRESS: + return -EBUSY; default: return -ENODEV; } @@ -326,11 +330,12 @@ static int ap_init_queue(ap_qid_t qid) i = AP_MAX_RESET; /* return with -ENODEV */ break; case AP_RESPONSE_RESET_IN_PROGRESS: + rc = -EBUSY; case AP_RESPONSE_BUSY: default: break; } - if (rc != -ENODEV) + if (rc != -ENODEV && rc != -EBUSY) break; if (i < AP_MAX_RESET - 1) { udelay(5); @@ -341,6 +346,40 @@ static int ap_init_queue(ap_qid_t qid) } /** + * Arm request timeout if a AP device was idle and a new request is submitted. + */ +static void ap_increase_queue_count(struct ap_device *ap_dev) +{ + int timeout = ap_dev->drv->request_timeout; + + ap_dev->queue_count++; + if (ap_dev->queue_count == 1) { + mod_timer(&ap_dev->timeout, jiffies + timeout); + ap_dev->reset = AP_RESET_ARMED; + } +} + +/** + * AP device is still alive, re-schedule request timeout if there are still + * pending requests. + */ +static void ap_decrease_queue_count(struct ap_device *ap_dev) +{ + int timeout = ap_dev->drv->request_timeout; + + ap_dev->queue_count--; + if (ap_dev->queue_count > 0) + mod_timer(&ap_dev->timeout, jiffies + timeout); + else + /** + * The timeout timer should to be disabled now - since + * del_timer_sync() is very expensive, we just tell via the + * reset flag to ignore the pending timeout timer. + */ + ap_dev->reset = AP_RESET_IGNORE; +} + +/** * AP device related attributes. */ static ssize_t ap_hwtype_show(struct device *dev, @@ -498,6 +537,7 @@ static int ap_device_remove(struct device *dev) struct ap_driver *ap_drv = ap_dev->drv; ap_flush_queue(ap_dev); + del_timer_sync(&ap_dev->timeout); if (ap_drv->remove) ap_drv->remove(ap_dev); spin_lock_bh(&ap_device_lock); @@ -759,17 +799,21 @@ static void ap_scan_bus(struct work_struct *unused) __ap_scan_bus); rc = ap_query_queue(qid, &queue_depth, &device_type); if (dev) { + if (rc == -EBUSY) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(AP_RESET_TIMEOUT); + rc = ap_query_queue(qid, &queue_depth, + &device_type); + } ap_dev = to_ap_dev(dev); spin_lock_bh(&ap_dev->lock); if (rc || ap_dev->unregistered) { spin_unlock_bh(&ap_dev->lock); - put_device(dev); device_unregister(dev); + put_device(dev); continue; - } else - spin_unlock_bh(&ap_dev->lock); - } - if (dev) { + } + spin_unlock_bh(&ap_dev->lock); put_device(dev); continue; } @@ -788,6 +832,8 @@ static void ap_scan_bus(struct work_struct *unused) INIT_LIST_HEAD(&ap_dev->pendingq); INIT_LIST_HEAD(&ap_dev->requestq); INIT_LIST_HEAD(&ap_dev->list); + setup_timer(&ap_dev->timeout, ap_request_timeout, + (unsigned long) ap_dev); if (device_type == 0) ap_probe_device_type(ap_dev); else @@ -853,7 +899,7 @@ static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags) switch (status.response_code) { case AP_RESPONSE_NORMAL: atomic_dec(&ap_poll_requests); - ap_dev->queue_count--; + ap_decrease_queue_count(ap_dev); list_for_each_entry(ap_msg, &ap_dev->pendingq, list) { if (ap_msg->psmid != ap_dev->reply->psmid) continue; @@ -904,7 +950,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) switch (status.response_code) { case AP_RESPONSE_NORMAL: atomic_inc(&ap_poll_requests); - ap_dev->queue_count++; + ap_increase_queue_count(ap_dev); list_move_tail(&ap_msg->list, &ap_dev->pendingq); ap_dev->requestq_count--; ap_dev->pendingq_count++; @@ -914,6 +960,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) *flags |= 2; break; case AP_RESPONSE_Q_FULL: + case AP_RESPONSE_RESET_IN_PROGRESS: *flags |= 2; break; case AP_RESPONSE_MESSAGE_TOO_BIG: @@ -960,10 +1007,11 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms list_add_tail(&ap_msg->list, &ap_dev->pendingq); atomic_inc(&ap_poll_requests); ap_dev->pendingq_count++; - ap_dev->queue_count++; + ap_increase_queue_count(ap_dev); ap_dev->total_request_count++; break; case AP_RESPONSE_Q_FULL: + case AP_RESPONSE_RESET_IN_PROGRESS: list_add_tail(&ap_msg->list, &ap_dev->requestq); ap_dev->requestq_count++; ap_dev->total_request_count++; @@ -1046,6 +1094,25 @@ static void ap_poll_timeout(unsigned long unused) } /** + * Reset a not responding AP device and move all requests from the + * pending queue to the request queue. + */ +static void ap_reset(struct ap_device *ap_dev) +{ + int rc; + + ap_dev->reset = AP_RESET_IGNORE; + atomic_sub(ap_dev->queue_count, &ap_poll_requests); + ap_dev->queue_count = 0; + list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); + ap_dev->requestq_count += ap_dev->pendingq_count; + ap_dev->pendingq_count = 0; + rc = ap_init_queue(ap_dev->qid); + if (rc == -ENODEV) + ap_dev->unregistered = 1; +} + +/** * Poll all AP devices on the bus in a round robin fashion. Continue * polling until bit 2^0 of the control flags is not set. If bit 2^1 * of the control flags has been set arm the poll timer. @@ -1056,6 +1123,8 @@ static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags) if (!ap_dev->unregistered) { if (ap_poll_queue(ap_dev, flags)) ap_dev->unregistered = 1; + if (ap_dev->reset == AP_RESET_DO) + ap_reset(ap_dev); } spin_unlock(&ap_dev->lock); return 0; @@ -1147,6 +1216,17 @@ static void ap_poll_thread_stop(void) mutex_unlock(&ap_poll_thread_mutex); } +/** + * Handling of request timeouts + */ +static void ap_request_timeout(unsigned long data) +{ + struct ap_device *ap_dev = (struct ap_device *) data; + + if (ap_dev->reset == AP_RESET_ARMED) + ap_dev->reset = AP_RESET_DO; +} + static void ap_reset_domain(void) { int i; diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 008559ea742..87c2d644287 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -33,6 +33,7 @@ #define AP_DEVICES 64 /* Number of AP devices. */ #define AP_DOMAINS 16 /* Number of AP domains. */ #define AP_MAX_RESET 90 /* Maximum number of resets. */ +#define AP_RESET_TIMEOUT (HZ/2) /* Time in ticks for reset timeouts. */ #define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ #define AP_POLL_TIME 1 /* Time in ticks between receive polls. */ @@ -83,6 +84,13 @@ struct ap_queue_status { #define AP_DEVICE_TYPE_CEX2A 6 #define AP_DEVICE_TYPE_CEX2C 7 +/** + * AP reset flag states + */ +#define AP_RESET_IGNORE 0 /* request timeout will be ignored */ +#define AP_RESET_ARMED 1 /* request timeout timer is active */ +#define AP_RESET_DO 2 /* AP reset required */ + struct ap_device; struct ap_message; @@ -95,6 +103,7 @@ struct ap_driver { /* receive is called from tasklet context */ void (*receive)(struct ap_device *, struct ap_message *, struct ap_message *); + int request_timeout; /* request timeout in jiffies */ }; #define to_ap_drv(x) container_of((x), struct ap_driver, driver) @@ -112,6 +121,8 @@ struct ap_device { int queue_depth; /* AP queue depth.*/ int device_type; /* AP device type. */ int unregistered; /* marks AP device as unregistered */ + struct timer_list timeout; /* Timer for request timeouts. */ + int reset; /* Reset required after req. timeout. */ int queue_count; /* # messages currently on AP queue. */ diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 5bb13a9d089..08657f604b8 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -70,6 +70,7 @@ static struct ap_driver zcrypt_cex2a_driver = { .remove = zcrypt_cex2a_remove, .receive = zcrypt_cex2a_receive, .ids = zcrypt_cex2a_ids, + .request_timeout = CEX2A_CLEANUP_TIME, }; /** @@ -306,18 +307,13 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, CEX2A_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, mex->outputdata, mex->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: kfree(ap_msg.message); return rc; @@ -348,18 +344,13 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, CEX2A_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, crt->outputdata, crt->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: kfree(ap_msg.message); return rc; diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 818ffe05ac0..6e93b475178 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -70,6 +70,7 @@ static struct ap_driver zcrypt_pcica_driver = { .remove = zcrypt_pcica_remove, .receive = zcrypt_pcica_receive, .ids = zcrypt_pcica_ids, + .request_timeout = PCICA_CLEANUP_TIME, }; /** @@ -290,18 +291,13 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, PCICA_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, mex->outputdata, mex->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: kfree(ap_msg.message); return rc; @@ -332,18 +328,13 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, PCICA_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, crt->outputdata, crt->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: kfree(ap_msg.message); return rc; diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index f295a403b29..d6d59bf9ac3 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -82,6 +82,7 @@ static struct ap_driver zcrypt_pcicc_driver = { .remove = zcrypt_pcicc_remove, .receive = zcrypt_pcicc_receive, .ids = zcrypt_pcicc_ids, + .request_timeout = PCICC_CLEANUP_TIME, }; /** @@ -501,18 +502,13 @@ static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, PCICC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, mex->outputdata, mex->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: free_page((unsigned long) ap_msg.message); return rc; @@ -544,18 +540,13 @@ static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, PCICC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, crt->outputdata, crt->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: free_page((unsigned long) ap_msg.message); return rc; diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 252443b6bd1..64948788d30 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -93,6 +93,7 @@ static struct ap_driver zcrypt_pcixcc_driver = { .remove = zcrypt_pcixcc_remove, .receive = zcrypt_pcixcc_receive, .ids = zcrypt_pcixcc_ids, + .request_timeout = PCIXCC_CLEANUP_TIME, }; /** @@ -641,18 +642,13 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev, goto out_free; init_completion(&resp_type.work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &resp_type.work, PCIXCC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&resp_type.work); + if (rc == 0) rc = convert_response_ica(zdev, &ap_msg, mex->outputdata, mex->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: free_page((unsigned long) ap_msg.message); return rc; @@ -685,18 +681,13 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev, goto out_free; init_completion(&resp_type.work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &resp_type.work, PCIXCC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&resp_type.work); + if (rc == 0) rc = convert_response_ica(zdev, &ap_msg, crt->outputdata, crt->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: free_page((unsigned long) ap_msg.message); return rc; @@ -729,17 +720,12 @@ static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev, goto out_free; init_completion(&resp_type.work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &resp_type.work, PCIXCC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&resp_type.work); + if (rc == 0) rc = convert_response_xcrb(zdev, &ap_msg, xcRB); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: memset(ap_msg.message, 0x0, ap_msg.length); kfree(ap_msg.message); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 904e5aeb696..df95d6c2cef 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -35,4 +35,17 @@ config W1_SLAVE_DS2433_CRC Each block has 30 bytes of data and a two byte CRC16. Full block writes are only allowed if the CRC is valid. +config W1_SLAVE_DS2760 + tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)" + depends on W1 + help + If you enable this you will have the DS2760 battery monitor + chip support. + + The battery monitor chip is used in many batteries/devices + as the one who is responsible for charging/discharging/monitoring + Li+ batteries. + + If you are unsure, say N. + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 725dcfdfddb..a8eb7524df1 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o +obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c new file mode 100644 index 00000000000..88a37fbccc3 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2760.c @@ -0,0 +1,213 @@ +/* + * 1-Wire implementation for the ds2760 chip + * + * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/idr.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2760.h" + +static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count, + int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return 0; + + mutex_lock(&sl->master->mutex); + + if (addr > DS2760_DATA_SIZE || addr < 0) { + count = 0; + goto out; + } + if (addr + count > DS2760_DATA_SIZE) + count = DS2760_DATA_SIZE - addr; + + if (!w1_reset_select_slave(sl)) { + if (!io) { + w1_write_8(sl->master, W1_DS2760_READ_DATA); + w1_write_8(sl->master, addr); + count = w1_read_block(sl->master, buf, count); + } else { + w1_write_8(sl->master, W1_DS2760_WRITE_DATA); + w1_write_8(sl->master, addr); + w1_write_block(sl->master, buf, count); + /* XXX w1_write_block returns void, not n_written */ + } + } + +out: + mutex_unlock(&sl->master->mutex); + + return count; +} + +int w1_ds2760_read(struct device *dev, char *buf, int addr, size_t count) +{ + return w1_ds2760_io(dev, buf, addr, count, 0); +} + +int w1_ds2760_write(struct device *dev, char *buf, int addr, size_t count) +{ + return w1_ds2760_io(dev, buf, addr, count, 1); +} + +static ssize_t w1_ds2760_read_bin(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + return w1_ds2760_read(dev, buf, off, count); +} + +static struct bin_attribute w1_ds2760_bin_attr = { + .attr = { + .name = "w1_slave", + .mode = S_IRUGO, + .owner = THIS_MODULE, + }, + .size = DS2760_DATA_SIZE, + .read = w1_ds2760_read_bin, +}; + +static DEFINE_IDR(bat_idr); +static DEFINE_MUTEX(bat_idr_lock); + +static int new_bat_id(void) +{ + int ret; + + while (1) { + int id; + + ret = idr_pre_get(&bat_idr, GFP_KERNEL); + if (ret == 0) + return -ENOMEM; + + mutex_lock(&bat_idr_lock); + ret = idr_get_new(&bat_idr, NULL, &id); + mutex_unlock(&bat_idr_lock); + + if (ret == 0) { + ret = id & MAX_ID_MASK; + break; + } else if (ret == -EAGAIN) { + continue; + } else { + break; + } + } + + return ret; +} + +static void release_bat_id(int id) +{ + mutex_lock(&bat_idr_lock); + idr_remove(&bat_idr, id); + mutex_unlock(&bat_idr_lock); + + return; +} + +static int w1_ds2760_add_slave(struct w1_slave *sl) +{ + int ret; + int id; + struct platform_device *pdev; + + id = new_bat_id(); + if (id < 0) { + ret = id; + goto noid; + } + + pdev = platform_device_alloc("ds2760-battery", id); + if (!pdev) { + ret = -ENOMEM; + goto pdev_alloc_failed; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); + if (ret) + goto bin_attr_failed; + + dev_set_drvdata(&sl->dev, pdev); + + goto success; + +bin_attr_failed: +pdev_add_failed: + platform_device_unregister(pdev); +pdev_alloc_failed: + release_bat_id(id); +noid: +success: + return ret; +} + +static void w1_ds2760_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + int id = pdev->id; + + platform_device_unregister(pdev); + release_bat_id(id); + sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); + + return; +} + +static struct w1_family_ops w1_ds2760_fops = { + .add_slave = w1_ds2760_add_slave, + .remove_slave = w1_ds2760_remove_slave, +}; + +static struct w1_family w1_ds2760_family = { + .fid = W1_FAMILY_DS2760, + .fops = &w1_ds2760_fops, +}; + +static int __init w1_ds2760_init(void) +{ + printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor " + " chip - (c) 2004-2005, Szabolcs Gyurko\n"); + idr_init(&bat_idr); + return w1_register_family(&w1_ds2760_family); +} + +static void __exit w1_ds2760_exit(void) +{ + w1_unregister_family(&w1_ds2760_family); + idr_destroy(&bat_idr); +} + +EXPORT_SYMBOL(w1_ds2760_read); +EXPORT_SYMBOL(w1_ds2760_write); + +module_init(w1_ds2760_init); +module_exit(w1_ds2760_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>"); +MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip"); diff --git a/drivers/w1/slaves/w1_ds2760.h b/drivers/w1/slaves/w1_ds2760.h new file mode 100644 index 00000000000..f1302429cb0 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2760.h @@ -0,0 +1,50 @@ +/* + * 1-Wire implementation for the ds2760 chip + * + * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + */ + +#ifndef __w1_ds2760_h__ +#define __w1_ds2760_h__ + +/* Known commands to the DS2760 chip */ +#define W1_DS2760_SWAP 0xAA +#define W1_DS2760_READ_DATA 0x69 +#define W1_DS2760_WRITE_DATA 0x6C +#define W1_DS2760_COPY_DATA 0x48 +#define W1_DS2760_RECALL_DATA 0xB8 +#define W1_DS2760_LOCK 0x6A + +/* Number of valid register addresses */ +#define DS2760_DATA_SIZE 0x40 + +#define DS2760_PROTECTION_REG 0x00 +#define DS2760_STATUS_REG 0x01 +#define DS2760_EEPROM_REG 0x07 +#define DS2760_SPECIAL_FEATURE_REG 0x08 +#define DS2760_VOLTAGE_MSB 0x0c +#define DS2760_VOLTAGE_LSB 0x0d +#define DS2760_CURRENT_MSB 0x0e +#define DS2760_CURRENT_LSB 0x0f +#define DS2760_CURRENT_ACCUM_MSB 0x10 +#define DS2760_CURRENT_ACCUM_LSB 0x11 +#define DS2760_TEMP_MSB 0x18 +#define DS2760_TEMP_LSB 0x19 +#define DS2760_EEPROM_BLOCK0 0x20 +#define DS2760_ACTIVE_FULL 0x20 +#define DS2760_EEPROM_BLOCK1 0x30 +#define DS2760_RATED_CAPACITY 0x32 +#define DS2760_CURRENT_OFFSET_BIAS 0x33 +#define DS2760_ACTIVE_EMPTY 0x3b + +extern int w1_ds2760_read(struct device *dev, char *buf, int addr, + size_t count); +extern int w1_ds2760_write(struct device *dev, char *buf, int addr, + size_t count); + +#endif /* !__w1_ds2760_h__ */ diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 1e2ac40c2c1..ef1e1dafa19 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -33,6 +33,7 @@ #define W1_THERM_DS1822 0x22 #define W1_EEPROM_DS2433 0x23 #define W1_THERM_DS18B20 0x28 +#define W1_FAMILY_DS2760 0x30 #define MAXNAMELEN 32 diff --git a/fs/adfs/file.c b/fs/adfs/file.c index f544a285592..36e381c6a99 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -33,7 +33,7 @@ const struct file_operations adfs_file_operations = { .fsync = file_fsync, .write = do_sync_write, .aio_write = generic_file_aio_write, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; const struct inode_operations adfs_file_inode_operations = { diff --git a/fs/affs/file.c b/fs/affs/file.c index c8796906f58..c314a35f091 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -35,7 +35,7 @@ const struct file_operations affs_file_operations = { .open = affs_file_open, .release = affs_file_release, .fsync = file_fsync, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; const struct inode_operations affs_file_inode_operations = { diff --git a/fs/afs/file.c b/fs/afs/file.c index 9c0e721d9fc..aede7eb66dd 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -32,7 +32,7 @@ const struct file_operations afs_file_operations = { .aio_read = generic_file_aio_read, .aio_write = afs_file_write, .mmap = generic_file_readonly_mmap, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, .fsync = afs_fsync, }; diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 329ee473eed..521ff7caadb 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -114,12 +114,6 @@ static int bad_file_lock(struct file *file, int cmd, struct file_lock *fl) return -EIO; } -static ssize_t bad_file_sendfile(struct file *in_file, loff_t *ppos, - size_t count, read_actor_t actor, void *target) -{ - return -EIO; -} - static ssize_t bad_file_sendpage(struct file *file, struct page *page, int off, size_t len, loff_t *pos, int more) { @@ -182,7 +176,6 @@ static const struct file_operations bad_file_ops = .aio_fsync = bad_file_aio_fsync, .fasync = bad_file_fasync, .lock = bad_file_lock, - .sendfile = bad_file_sendfile, .sendpage = bad_file_sendpage, .get_unmapped_area = bad_file_get_unmapped_area, .check_flags = bad_file_check_flags, diff --git a/fs/bfs/file.c b/fs/bfs/file.c index ef4d1fa04e6..24310e9ee05 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -24,7 +24,7 @@ const struct file_operations bfs_file_operations = { .write = do_sync_write, .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; static int bfs_move_block(unsigned long from, unsigned long to, struct super_block *sb) @@ -1223,8 +1223,6 @@ EXPORT_SYMBOL(bio_hw_segments); EXPORT_SYMBOL(bio_add_page); EXPORT_SYMBOL(bio_add_pc_page); EXPORT_SYMBOL(bio_get_nr_vecs); -EXPORT_SYMBOL(bio_map_user); -EXPORT_SYMBOL(bio_unmap_user); EXPORT_SYMBOL(bio_map_kern); EXPORT_SYMBOL(bio_pair_release); EXPORT_SYMBOL(bio_split); diff --git a/fs/block_dev.c b/fs/block_dev.c index ea1480a16f5..b3e9bfa748c 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1346,7 +1346,6 @@ const struct file_operations def_blk_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = compat_blkdev_ioctl, #endif - .sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 7c04752b76c..8b0cbf4a4ad 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -616,7 +616,7 @@ const struct file_operations cifs_file_ops = { .fsync = cifs_fsync, .flush = cifs_flush, .mmap = cifs_file_mmap, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, .llseek = cifs_llseek, #ifdef CONFIG_CIFS_POSIX .ioctl = cifs_ioctl, @@ -637,7 +637,7 @@ const struct file_operations cifs_file_direct_ops = { .lock = cifs_lock, .fsync = cifs_fsync, .flush = cifs_flush, - .sendfile = generic_file_sendfile, /* BB removeme BB */ + .splice_read = generic_file_splice_read, #ifdef CONFIG_CIFS_POSIX .ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ @@ -656,7 +656,7 @@ const struct file_operations cifs_file_nobrl_ops = { .fsync = cifs_fsync, .flush = cifs_flush, .mmap = cifs_file_mmap, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, .llseek = cifs_llseek, #ifdef CONFIG_CIFS_POSIX .ioctl = cifs_ioctl, @@ -676,7 +676,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = { .release = cifs_close, .fsync = cifs_fsync, .flush = cifs_flush, - .sendfile = generic_file_sendfile, /* BB removeme BB */ + .splice_read = generic_file_splice_read, #ifdef CONFIG_CIFS_POSIX .ioctl = cifs_ioctl, #endif /* CONFIG_CIFS_POSIX */ diff --git a/fs/coda/file.c b/fs/coda/file.c index 5ef2b609ec7..99dbe866816 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -47,8 +47,9 @@ coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *p } static ssize_t -coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count, - read_actor_t actor, void *target) +coda_file_splice_read(struct file *coda_file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t count, + unsigned int flags) { struct coda_file_info *cfi; struct file *host_file; @@ -57,10 +58,10 @@ coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count, BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; - if (!host_file->f_op || !host_file->f_op->sendfile) + if (!host_file->f_op || !host_file->f_op->splice_read) return -EINVAL; - return host_file->f_op->sendfile(host_file, ppos, count, actor, target); + return host_file->f_op->splice_read(host_file, ppos, pipe, count,flags); } static ssize_t @@ -295,6 +296,6 @@ const struct file_operations coda_file_operations = { .flush = coda_flush, .release = coda_release, .fsync = coda_fsync, - .sendfile = coda_file_sendfile, + .splice_read = coda_file_splice_read, }; diff --git a/fs/dlm/Makefile b/fs/dlm/Makefile index 604cf7dc5f3..d248e60951b 100644 --- a/fs/dlm/Makefile +++ b/fs/dlm/Makefile @@ -8,6 +8,7 @@ dlm-y := ast.o \ member.o \ memory.o \ midcomms.o \ + netlink.o \ lowcomms.o \ rcom.o \ recover.o \ diff --git a/fs/dlm/config.c b/fs/dlm/config.c index 822abdcd143..5069b2cb5a1 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -90,6 +90,7 @@ struct cluster { unsigned int cl_scan_secs; unsigned int cl_log_debug; unsigned int cl_protocol; + unsigned int cl_timewarn_cs; }; enum { @@ -103,6 +104,7 @@ enum { CLUSTER_ATTR_SCAN_SECS, CLUSTER_ATTR_LOG_DEBUG, CLUSTER_ATTR_PROTOCOL, + CLUSTER_ATTR_TIMEWARN_CS, }; struct cluster_attribute { @@ -162,6 +164,7 @@ CLUSTER_ATTR(toss_secs, 1); CLUSTER_ATTR(scan_secs, 1); CLUSTER_ATTR(log_debug, 0); CLUSTER_ATTR(protocol, 0); +CLUSTER_ATTR(timewarn_cs, 1); static struct configfs_attribute *cluster_attrs[] = { [CLUSTER_ATTR_TCP_PORT] = &cluster_attr_tcp_port.attr, @@ -174,6 +177,7 @@ static struct configfs_attribute *cluster_attrs[] = { [CLUSTER_ATTR_SCAN_SECS] = &cluster_attr_scan_secs.attr, [CLUSTER_ATTR_LOG_DEBUG] = &cluster_attr_log_debug.attr, [CLUSTER_ATTR_PROTOCOL] = &cluster_attr_protocol.attr, + [CLUSTER_ATTR_TIMEWARN_CS] = &cluster_attr_timewarn_cs.attr, NULL, }; @@ -429,6 +433,8 @@ static struct config_group *make_cluster(struct config_group *g, cl->cl_toss_secs = dlm_config.ci_toss_secs; cl->cl_scan_secs = dlm_config.ci_scan_secs; cl->cl_log_debug = dlm_config.ci_log_debug; + cl->cl_protocol = dlm_config.ci_protocol; + cl->cl_timewarn_cs = dlm_config.ci_timewarn_cs; space_list = &sps->ss_group; comm_list = &cms->cs_group; @@ -748,9 +754,16 @@ static ssize_t node_weight_write(struct node *nd, const char *buf, size_t len) static struct space *get_space(char *name) { + struct config_item *i; + if (!space_list) return NULL; - return to_space(config_group_find_obj(space_list, name)); + + down(&space_list->cg_subsys->su_sem); + i = config_group_find_obj(space_list, name); + up(&space_list->cg_subsys->su_sem); + + return to_space(i); } static void put_space(struct space *sp) @@ -776,20 +789,20 @@ static struct comm *get_comm(int nodeid, struct sockaddr_storage *addr) if (cm->nodeid != nodeid) continue; found = 1; + config_item_get(i); break; } else { if (!cm->addr_count || memcmp(cm->addr[0], addr, sizeof(*addr))) continue; found = 1; + config_item_get(i); break; } } up(&clusters_root.subsys.su_sem); - if (found) - config_item_get(i); - else + if (!found) cm = NULL; return cm; } @@ -909,6 +922,7 @@ int dlm_our_addr(struct sockaddr_storage *addr, int num) #define DEFAULT_SCAN_SECS 5 #define DEFAULT_LOG_DEBUG 0 #define DEFAULT_PROTOCOL 0 +#define DEFAULT_TIMEWARN_CS 500 /* 5 sec = 500 centiseconds */ struct dlm_config_info dlm_config = { .ci_tcp_port = DEFAULT_TCP_PORT, @@ -920,6 +934,7 @@ struct dlm_config_info dlm_config = { .ci_toss_secs = DEFAULT_TOSS_SECS, .ci_scan_secs = DEFAULT_SCAN_SECS, .ci_log_debug = DEFAULT_LOG_DEBUG, - .ci_protocol = DEFAULT_PROTOCOL + .ci_protocol = DEFAULT_PROTOCOL, + .ci_timewarn_cs = DEFAULT_TIMEWARN_CS }; diff --git a/fs/dlm/config.h b/fs/dlm/config.h index 967cc3d72e5..a3170fe2209 100644 --- a/fs/dlm/config.h +++ b/fs/dlm/config.h @@ -27,6 +27,7 @@ struct dlm_config_info { int ci_scan_secs; int ci_log_debug; int ci_protocol; + int ci_timewarn_cs; }; extern struct dlm_config_info dlm_config; diff --git a/fs/dlm/debug_fs.c b/fs/dlm/debug_fs.c index 61ba670b9e0..12c3bfd5e66 100644 --- a/fs/dlm/debug_fs.c +++ b/fs/dlm/debug_fs.c @@ -17,6 +17,7 @@ #include <linux/debugfs.h> #include "dlm_internal.h" +#include "lock.h" #define DLM_DEBUG_BUF_LEN 4096 static char debug_buf[DLM_DEBUG_BUF_LEN]; @@ -26,6 +27,8 @@ static struct dentry *dlm_root; struct rsb_iter { int entry; + int locks; + int header; struct dlm_ls *ls; struct list_head *next; struct dlm_rsb *rsb; @@ -57,8 +60,8 @@ static char *print_lockmode(int mode) } } -static void print_lock(struct seq_file *s, struct dlm_lkb *lkb, - struct dlm_rsb *res) +static void print_resource_lock(struct seq_file *s, struct dlm_lkb *lkb, + struct dlm_rsb *res) { seq_printf(s, "%08x %s", lkb->lkb_id, print_lockmode(lkb->lkb_grmode)); @@ -85,6 +88,8 @@ static int print_resource(struct dlm_rsb *res, struct seq_file *s) struct dlm_lkb *lkb; int i, lvblen = res->res_ls->ls_lvblen, recover_list, root_list; + lock_rsb(res); + seq_printf(s, "\nResource %p Name (len=%d) \"", res, res->res_length); for (i = 0; i < res->res_length; i++) { if (isprint(res->res_name[i])) @@ -129,15 +134,15 @@ static int print_resource(struct dlm_rsb *res, struct seq_file *s) /* Print the locks attached to this resource */ seq_printf(s, "Granted Queue\n"); list_for_each_entry(lkb, &res->res_grantqueue, lkb_statequeue) - print_lock(s, lkb, res); + print_resource_lock(s, lkb, res); seq_printf(s, "Conversion Queue\n"); list_for_each_entry(lkb, &res->res_convertqueue, lkb_statequeue) - print_lock(s, lkb, res); + print_resource_lock(s, lkb, res); seq_printf(s, "Waiting Queue\n"); list_for_each_entry(lkb, &res->res_waitqueue, lkb_statequeue) - print_lock(s, lkb, res); + print_resource_lock(s, lkb, res); if (list_empty(&res->res_lookup)) goto out; @@ -151,6 +156,61 @@ static int print_resource(struct dlm_rsb *res, struct seq_file *s) seq_printf(s, "\n"); } out: + unlock_rsb(res); + return 0; +} + +static void print_lock(struct seq_file *s, struct dlm_lkb *lkb, struct dlm_rsb *r) +{ + struct dlm_user_args *ua; + unsigned int waiting = 0; + uint64_t xid = 0; + + if (lkb->lkb_flags & DLM_IFL_USER) { + ua = (struct dlm_user_args *) lkb->lkb_astparam; + if (ua) + xid = ua->xid; + } + + if (lkb->lkb_timestamp) + waiting = jiffies_to_msecs(jiffies - lkb->lkb_timestamp); + + /* id nodeid remid pid xid exflags flags sts grmode rqmode time_ms + r_nodeid r_len r_name */ + + seq_printf(s, "%x %d %x %u %llu %x %x %d %d %d %u %u %d \"%s\"\n", + lkb->lkb_id, + lkb->lkb_nodeid, + lkb->lkb_remid, + lkb->lkb_ownpid, + (unsigned long long)xid, + lkb->lkb_exflags, + lkb->lkb_flags, + lkb->lkb_status, + lkb->lkb_grmode, + lkb->lkb_rqmode, + waiting, + r->res_nodeid, + r->res_length, + r->res_name); +} + +static int print_locks(struct dlm_rsb *r, struct seq_file *s) +{ + struct dlm_lkb *lkb; + + lock_rsb(r); + + list_for_each_entry(lkb, &r->res_grantqueue, lkb_statequeue) + print_lock(s, lkb, r); + + list_for_each_entry(lkb, &r->res_convertqueue, lkb_statequeue) + print_lock(s, lkb, r); + + list_for_each_entry(lkb, &r->res_waitqueue, lkb_statequeue) + print_lock(s, lkb, r); + + unlock_rsb(r); return 0; } @@ -166,6 +226,9 @@ static int rsb_iter_next(struct rsb_iter *ri) read_lock(&ls->ls_rsbtbl[i].lock); if (!list_empty(&ls->ls_rsbtbl[i].list)) { ri->next = ls->ls_rsbtbl[i].list.next; + ri->rsb = list_entry(ri->next, struct dlm_rsb, + res_hashchain); + dlm_hold_rsb(ri->rsb); read_unlock(&ls->ls_rsbtbl[i].lock); break; } @@ -176,6 +239,7 @@ static int rsb_iter_next(struct rsb_iter *ri) if (ri->entry >= ls->ls_rsbtbl_size) return 1; } else { + struct dlm_rsb *old = ri->rsb; i = ri->entry; read_lock(&ls->ls_rsbtbl[i].lock); ri->next = ri->next->next; @@ -184,11 +248,14 @@ static int rsb_iter_next(struct rsb_iter *ri) ri->next = NULL; ri->entry++; read_unlock(&ls->ls_rsbtbl[i].lock); + dlm_put_rsb(old); goto top; } + ri->rsb = list_entry(ri->next, struct dlm_rsb, res_hashchain); + dlm_hold_rsb(ri->rsb); read_unlock(&ls->ls_rsbtbl[i].lock); + dlm_put_rsb(old); } - ri->rsb = list_entry(ri->next, struct dlm_rsb, res_hashchain); return 0; } @@ -202,7 +269,7 @@ static struct rsb_iter *rsb_iter_init(struct dlm_ls *ls) { struct rsb_iter *ri; - ri = kmalloc(sizeof *ri, GFP_KERNEL); + ri = kzalloc(sizeof *ri, GFP_KERNEL); if (!ri) return NULL; @@ -260,7 +327,17 @@ static int rsb_seq_show(struct seq_file *file, void *iter_ptr) { struct rsb_iter *ri = iter_ptr; - print_resource(ri->rsb, file); + if (ri->locks) { + if (ri->header) { + seq_printf(file, "id nodeid remid pid xid exflags flags " + "sts grmode rqmode time_ms r_nodeid " + "r_len r_name\n"); + ri->header = 0; + } + print_locks(ri->rsb, file); + } else { + print_resource(ri->rsb, file); + } return 0; } @@ -296,6 +373,83 @@ static const struct file_operations rsb_fops = { }; /* + * Dump state in compact per-lock listing + */ + +static struct rsb_iter *locks_iter_init(struct dlm_ls *ls, loff_t *pos) +{ + struct rsb_iter *ri; + + ri = kzalloc(sizeof *ri, GFP_KERNEL); + if (!ri) + return NULL; + + ri->ls = ls; + ri->entry = 0; + ri->next = NULL; + ri->locks = 1; + + if (*pos == 0) + ri->header = 1; + + if (rsb_iter_next(ri)) { + rsb_iter_free(ri); + return NULL; + } + + return ri; +} + +static void *locks_seq_start(struct seq_file *file, loff_t *pos) +{ + struct rsb_iter *ri; + loff_t n = *pos; + + ri = locks_iter_init(file->private, pos); + if (!ri) + return NULL; + + while (n--) { + if (rsb_iter_next(ri)) { + rsb_iter_free(ri); + return NULL; + } + } + + return ri; +} + +static struct seq_operations locks_seq_ops = { + .start = locks_seq_start, + .next = rsb_seq_next, + .stop = rsb_seq_stop, + .show = rsb_seq_show, +}; + +static int locks_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + int ret; + + ret = seq_open(file, &locks_seq_ops); + if (ret) + return ret; + + seq = file->private_data; + seq->private = inode->i_private; + + return 0; +} + +static const struct file_operations locks_fops = { + .owner = THIS_MODULE, + .open = locks_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + +/* * dump lkb's on the ls_waiters list */ @@ -362,6 +516,20 @@ int dlm_create_debug_file(struct dlm_ls *ls) return -ENOMEM; } + memset(name, 0, sizeof(name)); + snprintf(name, DLM_LOCKSPACE_LEN+8, "%s_locks", ls->ls_name); + + ls->ls_debug_locks_dentry = debugfs_create_file(name, + S_IFREG | S_IRUGO, + dlm_root, + ls, + &locks_fops); + if (!ls->ls_debug_locks_dentry) { + debugfs_remove(ls->ls_debug_waiters_dentry); + debugfs_remove(ls->ls_debug_rsb_dentry); + return -ENOMEM; + } + return 0; } @@ -371,6 +539,8 @@ void dlm_delete_debug_file(struct dlm_ls *ls) debugfs_remove(ls->ls_debug_rsb_dentry); if (ls->ls_debug_waiters_dentry) debugfs_remove(ls->ls_debug_waiters_dentry); + if (ls->ls_debug_locks_dentry) + debugfs_remove(ls->ls_debug_locks_dentry); } int dlm_register_debugfs(void) diff --git a/fs/dlm/dlm_internal.h b/fs/dlm/dlm_internal.h index 30994d68f6a..74901e981e1 100644 --- a/fs/dlm/dlm_internal.h +++ b/fs/dlm/dlm_internal.h @@ -151,6 +151,7 @@ struct dlm_args { void *bastaddr; int mode; struct dlm_lksb *lksb; + unsigned long timeout; }; @@ -213,6 +214,9 @@ struct dlm_args { #define DLM_IFL_OVERLAP_UNLOCK 0x00080000 #define DLM_IFL_OVERLAP_CANCEL 0x00100000 #define DLM_IFL_ENDOFLIFE 0x00200000 +#define DLM_IFL_WATCH_TIMEWARN 0x00400000 +#define DLM_IFL_TIMEOUT_CANCEL 0x00800000 +#define DLM_IFL_DEADLOCK_CANCEL 0x01000000 #define DLM_IFL_USER 0x00000001 #define DLM_IFL_ORPHAN 0x00000002 @@ -243,6 +247,9 @@ struct dlm_lkb { struct list_head lkb_wait_reply; /* waiting for remote reply */ struct list_head lkb_astqueue; /* need ast to be sent */ struct list_head lkb_ownqueue; /* list of locks for a process */ + struct list_head lkb_time_list; + unsigned long lkb_timestamp; + unsigned long lkb_timeout_cs; char *lkb_lvbptr; struct dlm_lksb *lkb_lksb; /* caller's status block */ @@ -447,12 +454,16 @@ struct dlm_ls { struct mutex ls_orphans_mutex; struct list_head ls_orphans; + struct mutex ls_timeout_mutex; + struct list_head ls_timeout; + struct list_head ls_nodes; /* current nodes in ls */ struct list_head ls_nodes_gone; /* dead node list, recovery */ int ls_num_nodes; /* number of nodes in ls */ int ls_low_nodeid; int ls_total_weight; int *ls_node_array; + gfp_t ls_allocation; struct dlm_rsb ls_stub_rsb; /* for returning errors */ struct dlm_lkb ls_stub_lkb; /* for returning errors */ @@ -460,9 +471,12 @@ struct dlm_ls { struct dentry *ls_debug_rsb_dentry; /* debugfs */ struct dentry *ls_debug_waiters_dentry; /* debugfs */ + struct dentry *ls_debug_locks_dentry; /* debugfs */ wait_queue_head_t ls_uevent_wait; /* user part of join/leave */ int ls_uevent_result; + struct completion ls_members_done; + int ls_members_result; struct miscdevice ls_device; @@ -472,6 +486,7 @@ struct dlm_ls { struct task_struct *ls_recoverd_task; struct mutex ls_recoverd_active; spinlock_t ls_recover_lock; + unsigned long ls_recover_begin; /* jiffies timestamp */ uint32_t ls_recover_status; /* DLM_RS_ */ uint64_t ls_recover_seq; struct dlm_recover *ls_recover_args; @@ -501,6 +516,7 @@ struct dlm_ls { #define LSFL_RCOM_READY 3 #define LSFL_RCOM_WAIT 4 #define LSFL_UEVENT_WAIT 5 +#define LSFL_TIMEWARN 6 /* much of this is just saving user space pointers associated with the lock that we pass back to the user lib with an ast */ @@ -518,6 +534,7 @@ struct dlm_user_args { void __user *castaddr; void __user *bastparam; void __user *bastaddr; + uint64_t xid; }; #define DLM_PROC_FLAGS_CLOSING 1 diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c index d8d6e729f96..b455919c199 100644 --- a/fs/dlm/lock.c +++ b/fs/dlm/lock.c @@ -82,10 +82,13 @@ static int send_bast(struct dlm_rsb *r, struct dlm_lkb *lkb, int mode); static int send_lookup(struct dlm_rsb *r, struct dlm_lkb *lkb); static int send_remove(struct dlm_rsb *r); static int _request_lock(struct dlm_rsb *r, struct dlm_lkb *lkb); +static int _cancel_lock(struct dlm_rsb *r, struct dlm_lkb *lkb); static void __receive_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, struct dlm_message *ms); static int receive_extralen(struct dlm_message *ms); static void do_purge(struct dlm_ls *ls, int nodeid, int pid); +static void del_timeout(struct dlm_lkb *lkb); +void dlm_timeout_warn(struct dlm_lkb *lkb); /* * Lock compatibilty matrix - thanks Steve @@ -194,17 +197,17 @@ void dlm_dump_rsb(struct dlm_rsb *r) /* Threads cannot use the lockspace while it's being recovered */ -static inline void lock_recovery(struct dlm_ls *ls) +static inline void dlm_lock_recovery(struct dlm_ls *ls) { down_read(&ls->ls_in_recovery); } -static inline void unlock_recovery(struct dlm_ls *ls) +void dlm_unlock_recovery(struct dlm_ls *ls) { up_read(&ls->ls_in_recovery); } -static inline int lock_recovery_try(struct dlm_ls *ls) +int dlm_lock_recovery_try(struct dlm_ls *ls) { return down_read_trylock(&ls->ls_in_recovery); } @@ -286,8 +289,22 @@ static void queue_cast(struct dlm_rsb *r, struct dlm_lkb *lkb, int rv) if (is_master_copy(lkb)) return; + del_timeout(lkb); + DLM_ASSERT(lkb->lkb_lksb, dlm_print_lkb(lkb);); + /* if the operation was a cancel, then return -DLM_ECANCEL, if a + timeout caused the cancel then return -ETIMEDOUT */ + if (rv == -DLM_ECANCEL && (lkb->lkb_flags & DLM_IFL_TIMEOUT_CANCEL)) { + lkb->lkb_flags &= ~DLM_IFL_TIMEOUT_CANCEL; + rv = -ETIMEDOUT; + } + + if (rv == -DLM_ECANCEL && (lkb->lkb_flags & DLM_IFL_DEADLOCK_CANCEL)) { + lkb->lkb_flags &= ~DLM_IFL_DEADLOCK_CANCEL; + rv = -EDEADLK; + } + lkb->lkb_lksb->sb_status = rv; lkb->lkb_lksb->sb_flags = lkb->lkb_sbflags; @@ -581,6 +598,7 @@ static int create_lkb(struct dlm_ls *ls, struct dlm_lkb **lkb_ret) kref_init(&lkb->lkb_ref); INIT_LIST_HEAD(&lkb->lkb_ownqueue); INIT_LIST_HEAD(&lkb->lkb_rsb_lookup); + INIT_LIST_HEAD(&lkb->lkb_time_list); get_random_bytes(&bucket, sizeof(bucket)); bucket &= (ls->ls_lkbtbl_size - 1); @@ -985,15 +1003,136 @@ void dlm_scan_rsbs(struct dlm_ls *ls) { int i; - if (dlm_locking_stopped(ls)) - return; - for (i = 0; i < ls->ls_rsbtbl_size; i++) { shrink_bucket(ls, i); + if (dlm_locking_stopped(ls)) + break; cond_resched(); } } +static void add_timeout(struct dlm_lkb *lkb) +{ + struct dlm_ls *ls = lkb->lkb_resource->res_ls; + + if (is_master_copy(lkb)) { + lkb->lkb_timestamp = jiffies; + return; + } + + if (test_bit(LSFL_TIMEWARN, &ls->ls_flags) && + !(lkb->lkb_exflags & DLM_LKF_NODLCKWT)) { + lkb->lkb_flags |= DLM_IFL_WATCH_TIMEWARN; + goto add_it; + } + if (lkb->lkb_exflags & DLM_LKF_TIMEOUT) + goto add_it; + return; + + add_it: + DLM_ASSERT(list_empty(&lkb->lkb_time_list), dlm_print_lkb(lkb);); + mutex_lock(&ls->ls_timeout_mutex); + hold_lkb(lkb); + lkb->lkb_timestamp = jiffies; + list_add_tail(&lkb->lkb_time_list, &ls->ls_timeout); + mutex_unlock(&ls->ls_timeout_mutex); +} + +static void del_timeout(struct dlm_lkb *lkb) +{ + struct dlm_ls *ls = lkb->lkb_resource->res_ls; + + mutex_lock(&ls->ls_timeout_mutex); + if (!list_empty(&lkb->lkb_time_list)) { + list_del_init(&lkb->lkb_time_list); + unhold_lkb(lkb); + } + mutex_unlock(&ls->ls_timeout_mutex); +} + +/* FIXME: is it safe to look at lkb_exflags, lkb_flags, lkb_timestamp, and + lkb_lksb_timeout without lock_rsb? Note: we can't lock timeout_mutex + and then lock rsb because of lock ordering in add_timeout. We may need + to specify some special timeout-related bits in the lkb that are just to + be accessed under the timeout_mutex. */ + +void dlm_scan_timeout(struct dlm_ls *ls) +{ + struct dlm_rsb *r; + struct dlm_lkb *lkb; + int do_cancel, do_warn; + + for (;;) { + if (dlm_locking_stopped(ls)) + break; + + do_cancel = 0; + do_warn = 0; + mutex_lock(&ls->ls_timeout_mutex); + list_for_each_entry(lkb, &ls->ls_timeout, lkb_time_list) { + + if ((lkb->lkb_exflags & DLM_LKF_TIMEOUT) && + time_after_eq(jiffies, lkb->lkb_timestamp + + lkb->lkb_timeout_cs * HZ/100)) + do_cancel = 1; + + if ((lkb->lkb_flags & DLM_IFL_WATCH_TIMEWARN) && + time_after_eq(jiffies, lkb->lkb_timestamp + + dlm_config.ci_timewarn_cs * HZ/100)) + do_warn = 1; + + if (!do_cancel && !do_warn) + continue; + hold_lkb(lkb); + break; + } + mutex_unlock(&ls->ls_timeout_mutex); + + if (!do_cancel && !do_warn) + break; + + r = lkb->lkb_resource; + hold_rsb(r); + lock_rsb(r); + + if (do_warn) { + /* clear flag so we only warn once */ + lkb->lkb_flags &= ~DLM_IFL_WATCH_TIMEWARN; + if (!(lkb->lkb_exflags & DLM_LKF_TIMEOUT)) + del_timeout(lkb); + dlm_timeout_warn(lkb); + } + + if (do_cancel) { + log_debug(ls, "timeout cancel %x node %d %s", + lkb->lkb_id, lkb->lkb_nodeid, r->res_name); + lkb->lkb_flags &= ~DLM_IFL_WATCH_TIMEWARN; + lkb->lkb_flags |= DLM_IFL_TIMEOUT_CANCEL; + del_timeout(lkb); + _cancel_lock(r, lkb); + } + + unlock_rsb(r); + unhold_rsb(r); + dlm_put_lkb(lkb); + } +} + +/* This is only called by dlm_recoverd, and we rely on dlm_ls_stop() stopping + dlm_recoverd before checking/setting ls_recover_begin. */ + +void dlm_adjust_timeouts(struct dlm_ls *ls) +{ + struct dlm_lkb *lkb; + long adj = jiffies - ls->ls_recover_begin; + + ls->ls_recover_begin = 0; + mutex_lock(&ls->ls_timeout_mutex); + list_for_each_entry(lkb, &ls->ls_timeout, lkb_time_list) + lkb->lkb_timestamp += adj; + mutex_unlock(&ls->ls_timeout_mutex); +} + /* lkb is master or local copy */ static void set_lvb_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) @@ -1275,10 +1414,8 @@ static int queue_conflict(struct list_head *head, struct dlm_lkb *lkb) * queue for one resource. The granted mode of each lock blocks the requested * mode of the other lock." * - * Part 2: if the granted mode of lkb is preventing the first lkb in the - * convert queue from being granted, then demote lkb (set grmode to NL). - * This second form requires that we check for conv-deadlk even when - * now == 0 in _can_be_granted(). + * Part 2: if the granted mode of lkb is preventing an earlier lkb in the + * convert queue from being granted, then deadlk/demote lkb. * * Example: * Granted Queue: empty @@ -1287,41 +1424,52 @@ static int queue_conflict(struct list_head *head, struct dlm_lkb *lkb) * * The first lock can't be granted because of the granted mode of the second * lock and the second lock can't be granted because it's not first in the - * list. We demote the granted mode of the second lock (the lkb passed to this - * function). + * list. We either cancel lkb's conversion (PR->EX) and return EDEADLK, or we + * demote the granted mode of lkb (from PR to NL) if it has the CONVDEADLK + * flag set and return DEMOTED in the lksb flags. + * + * Originally, this function detected conv-deadlk in a more limited scope: + * - if !modes_compat(lkb1, lkb2) && !modes_compat(lkb2, lkb1), or + * - if lkb1 was the first entry in the queue (not just earlier), and was + * blocked by the granted mode of lkb2, and there was nothing on the + * granted queue preventing lkb1 from being granted immediately, i.e. + * lkb2 was the only thing preventing lkb1 from being granted. + * + * That second condition meant we'd only say there was conv-deadlk if + * resolving it (by demotion) would lead to the first lock on the convert + * queue being granted right away. It allowed conversion deadlocks to exist + * between locks on the convert queue while they couldn't be granted anyway. * - * After the resolution, the "grant pending" function needs to go back and try - * to grant locks on the convert queue again since the first lock can now be - * granted. + * Now, we detect and take action on conversion deadlocks immediately when + * they're created, even if they may not be immediately consequential. If + * lkb1 exists anywhere in the convert queue and lkb2 comes in with a granted + * mode that would prevent lkb1's conversion from being granted, we do a + * deadlk/demote on lkb2 right away and don't let it onto the convert queue. + * I think this means that the lkb_is_ahead condition below should always + * be zero, i.e. there will never be conv-deadlk between two locks that are + * both already on the convert queue. */ -static int conversion_deadlock_detect(struct dlm_rsb *rsb, struct dlm_lkb *lkb) +static int conversion_deadlock_detect(struct dlm_rsb *r, struct dlm_lkb *lkb2) { - struct dlm_lkb *this, *first = NULL, *self = NULL; + struct dlm_lkb *lkb1; + int lkb_is_ahead = 0; - list_for_each_entry(this, &rsb->res_convertqueue, lkb_statequeue) { - if (!first) - first = this; - if (this == lkb) { - self = lkb; + list_for_each_entry(lkb1, &r->res_convertqueue, lkb_statequeue) { + if (lkb1 == lkb2) { + lkb_is_ahead = 1; continue; } - if (!modes_compat(this, lkb) && !modes_compat(lkb, this)) - return 1; - } - - /* if lkb is on the convert queue and is preventing the first - from being granted, then there's deadlock and we demote lkb. - multiple converting locks may need to do this before the first - converting lock can be granted. */ - - if (self && self != first) { - if (!modes_compat(lkb, first) && - !queue_conflict(&rsb->res_grantqueue, first)) - return 1; + if (!lkb_is_ahead) { + if (!modes_compat(lkb2, lkb1)) + return 1; + } else { + if (!modes_compat(lkb2, lkb1) && + !modes_compat(lkb1, lkb2)) + return 1; + } } - return 0; } @@ -1450,42 +1598,57 @@ static int _can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now) if (!now && !conv && list_empty(&r->res_convertqueue) && first_in_list(lkb, &r->res_waitqueue)) return 1; - out: - /* - * The following, enabled by CONVDEADLK, departs from VMS. - */ - - if (conv && (lkb->lkb_exflags & DLM_LKF_CONVDEADLK) && - conversion_deadlock_detect(r, lkb)) { - lkb->lkb_grmode = DLM_LOCK_NL; - lkb->lkb_sbflags |= DLM_SBF_DEMOTED; - } - return 0; } -/* - * The ALTPR and ALTCW flags aren't traditional lock manager flags, but are a - * simple way to provide a big optimization to applications that can use them. - */ - -static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now) +static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now, + int *err) { - uint32_t flags = lkb->lkb_exflags; int rv; int8_t alt = 0, rqmode = lkb->lkb_rqmode; + int8_t is_convert = (lkb->lkb_grmode != DLM_LOCK_IV); + + if (err) + *err = 0; rv = _can_be_granted(r, lkb, now); if (rv) goto out; - if (lkb->lkb_sbflags & DLM_SBF_DEMOTED) + /* + * The CONVDEADLK flag is non-standard and tells the dlm to resolve + * conversion deadlocks by demoting grmode to NL, otherwise the dlm + * cancels one of the locks. + */ + + if (is_convert && can_be_queued(lkb) && + conversion_deadlock_detect(r, lkb)) { + if (lkb->lkb_exflags & DLM_LKF_CONVDEADLK) { + lkb->lkb_grmode = DLM_LOCK_NL; + lkb->lkb_sbflags |= DLM_SBF_DEMOTED; + } else if (!(lkb->lkb_exflags & DLM_LKF_NODLCKWT)) { + if (err) + *err = -EDEADLK; + else { + log_print("can_be_granted deadlock %x now %d", + lkb->lkb_id, now); + dlm_dump_rsb(r); + } + } goto out; + } - if (rqmode != DLM_LOCK_PR && flags & DLM_LKF_ALTPR) + /* + * The ALTPR and ALTCW flags are non-standard and tell the dlm to try + * to grant a request in a mode other than the normal rqmode. It's a + * simple way to provide a big optimization to applications that can + * use them. + */ + + if (rqmode != DLM_LOCK_PR && (lkb->lkb_exflags & DLM_LKF_ALTPR)) alt = DLM_LOCK_PR; - else if (rqmode != DLM_LOCK_CW && flags & DLM_LKF_ALTCW) + else if (rqmode != DLM_LOCK_CW && (lkb->lkb_exflags & DLM_LKF_ALTCW)) alt = DLM_LOCK_CW; if (alt) { @@ -1500,10 +1663,20 @@ static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now) return rv; } +/* FIXME: I don't think that can_be_granted() can/will demote or find deadlock + for locks pending on the convert list. Once verified (watch for these + log_prints), we should be able to just call _can_be_granted() and not + bother with the demote/deadlk cases here (and there's no easy way to deal + with a deadlk here, we'd have to generate something like grant_lock with + the deadlk error.) */ + +/* returns the highest requested mode of all blocked conversions */ + static int grant_pending_convert(struct dlm_rsb *r, int high) { struct dlm_lkb *lkb, *s; int hi, demoted, quit, grant_restart, demote_restart; + int deadlk; quit = 0; restart: @@ -1513,14 +1686,29 @@ static int grant_pending_convert(struct dlm_rsb *r, int high) list_for_each_entry_safe(lkb, s, &r->res_convertqueue, lkb_statequeue) { demoted = is_demoted(lkb); - if (can_be_granted(r, lkb, 0)) { + deadlk = 0; + + if (can_be_granted(r, lkb, 0, &deadlk)) { grant_lock_pending(r, lkb); grant_restart = 1; - } else { - hi = max_t(int, lkb->lkb_rqmode, hi); - if (!demoted && is_demoted(lkb)) - demote_restart = 1; + continue; } + + if (!demoted && is_demoted(lkb)) { + log_print("WARN: pending demoted %x node %d %s", + lkb->lkb_id, lkb->lkb_nodeid, r->res_name); + demote_restart = 1; + continue; + } + + if (deadlk) { + log_print("WARN: pending deadlock %x node %d %s", + lkb->lkb_id, lkb->lkb_nodeid, r->res_name); + dlm_dump_rsb(r); + continue; + } + + hi = max_t(int, lkb->lkb_rqmode, hi); } if (grant_restart) @@ -1538,7 +1726,7 @@ static int grant_pending_wait(struct dlm_rsb *r, int high) struct dlm_lkb *lkb, *s; list_for_each_entry_safe(lkb, s, &r->res_waitqueue, lkb_statequeue) { - if (can_be_granted(r, lkb, 0)) + if (can_be_granted(r, lkb, 0, NULL)) grant_lock_pending(r, lkb); else high = max_t(int, lkb->lkb_rqmode, high); @@ -1733,7 +1921,7 @@ static void confirm_master(struct dlm_rsb *r, int error) } static int set_lock_args(int mode, struct dlm_lksb *lksb, uint32_t flags, - int namelen, uint32_t parent_lkid, void *ast, + int namelen, unsigned long timeout_cs, void *ast, void *astarg, void *bast, struct dlm_args *args) { int rv = -EINVAL; @@ -1776,10 +1964,6 @@ static int set_lock_args(int mode, struct dlm_lksb *lksb, uint32_t flags, if (flags & DLM_LKF_VALBLK && !lksb->sb_lvbptr) goto out; - /* parent/child locks not yet supported */ - if (parent_lkid) - goto out; - if (flags & DLM_LKF_CONVERT && !lksb->sb_lkid) goto out; @@ -1791,6 +1975,7 @@ static int set_lock_args(int mode, struct dlm_lksb *lksb, uint32_t flags, args->astaddr = ast; args->astparam = (long) astarg; args->bastaddr = bast; + args->timeout = timeout_cs; args->mode = mode; args->lksb = lksb; rv = 0; @@ -1845,6 +2030,7 @@ static int validate_lock_args(struct dlm_ls *ls, struct dlm_lkb *lkb, lkb->lkb_lksb = args->lksb; lkb->lkb_lvbptr = args->lksb->sb_lvbptr; lkb->lkb_ownpid = (int) current->pid; + lkb->lkb_timeout_cs = args->timeout; rv = 0; out: return rv; @@ -1903,6 +2089,9 @@ static int validate_unlock_args(struct dlm_lkb *lkb, struct dlm_args *args) if (is_overlap(lkb)) goto out; + /* don't let scand try to do a cancel */ + del_timeout(lkb); + if (lkb->lkb_flags & DLM_IFL_RESEND) { lkb->lkb_flags |= DLM_IFL_OVERLAP_CANCEL; rv = -EBUSY; @@ -1934,6 +2123,9 @@ static int validate_unlock_args(struct dlm_lkb *lkb, struct dlm_args *args) if (is_overlap_unlock(lkb)) goto out; + /* don't let scand try to do a cancel */ + del_timeout(lkb); + if (lkb->lkb_flags & DLM_IFL_RESEND) { lkb->lkb_flags |= DLM_IFL_OVERLAP_UNLOCK; rv = -EBUSY; @@ -1984,7 +2176,7 @@ static int do_request(struct dlm_rsb *r, struct dlm_lkb *lkb) { int error = 0; - if (can_be_granted(r, lkb, 1)) { + if (can_be_granted(r, lkb, 1, NULL)) { grant_lock(r, lkb); queue_cast(r, lkb, 0); goto out; @@ -1994,6 +2186,7 @@ static int do_request(struct dlm_rsb *r, struct dlm_lkb *lkb) error = -EINPROGRESS; add_lkb(r, lkb, DLM_LKSTS_WAITING); send_blocking_asts(r, lkb); + add_timeout(lkb); goto out; } @@ -2009,16 +2202,32 @@ static int do_request(struct dlm_rsb *r, struct dlm_lkb *lkb) static int do_convert(struct dlm_rsb *r, struct dlm_lkb *lkb) { int error = 0; + int deadlk = 0; /* changing an existing lock may allow others to be granted */ - if (can_be_granted(r, lkb, 1)) { + if (can_be_granted(r, lkb, 1, &deadlk)) { grant_lock(r, lkb); queue_cast(r, lkb, 0); grant_pending_locks(r); goto out; } + /* can_be_granted() detected that this lock would block in a conversion + deadlock, so we leave it on the granted queue and return EDEADLK in + the ast for the convert. */ + + if (deadlk) { + /* it's left on the granted queue */ + log_debug(r->res_ls, "deadlock %x node %d sts%d g%d r%d %s", + lkb->lkb_id, lkb->lkb_nodeid, lkb->lkb_status, + lkb->lkb_grmode, lkb->lkb_rqmode, r->res_name); + revert_lock(r, lkb); + queue_cast(r, lkb, -EDEADLK); + error = -EDEADLK; + goto out; + } + /* is_demoted() means the can_be_granted() above set the grmode to NL, and left us on the granted queue. This auto-demotion (due to CONVDEADLK) might mean other locks, and/or this lock, are @@ -2041,6 +2250,7 @@ static int do_convert(struct dlm_rsb *r, struct dlm_lkb *lkb) del_lkb(r, lkb); add_lkb(r, lkb, DLM_LKSTS_CONVERT); send_blocking_asts(r, lkb); + add_timeout(lkb); goto out; } @@ -2274,7 +2484,7 @@ int dlm_lock(dlm_lockspace_t *lockspace, if (!ls) return -EINVAL; - lock_recovery(ls); + dlm_lock_recovery(ls); if (convert) error = find_lkb(ls, lksb->sb_lkid, &lkb); @@ -2284,7 +2494,7 @@ int dlm_lock(dlm_lockspace_t *lockspace, if (error) goto out; - error = set_lock_args(mode, lksb, flags, namelen, parent_lkid, ast, + error = set_lock_args(mode, lksb, flags, namelen, 0, ast, astarg, bast, &args); if (error) goto out_put; @@ -2299,10 +2509,10 @@ int dlm_lock(dlm_lockspace_t *lockspace, out_put: if (convert || error) __put_lkb(ls, lkb); - if (error == -EAGAIN) + if (error == -EAGAIN || error == -EDEADLK) error = 0; out: - unlock_recovery(ls); + dlm_unlock_recovery(ls); dlm_put_lockspace(ls); return error; } @@ -2322,7 +2532,7 @@ int dlm_unlock(dlm_lockspace_t *lockspace, if (!ls) return -EINVAL; - lock_recovery(ls); + dlm_lock_recovery(ls); error = find_lkb(ls, lkid, &lkb); if (error) @@ -2344,7 +2554,7 @@ int dlm_unlock(dlm_lockspace_t *lockspace, out_put: dlm_put_lkb(lkb); out: - unlock_recovery(ls); + dlm_unlock_recovery(ls); dlm_put_lockspace(ls); return error; } @@ -2384,7 +2594,7 @@ static int _create_message(struct dlm_ls *ls, int mb_len, pass into lowcomms_commit and a message buffer (mb) that we write our data into */ - mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, GFP_KERNEL, &mb); + mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, ls->ls_allocation, &mb); if (!mh) return -ENOBUFS; @@ -3111,9 +3321,10 @@ static void receive_request_reply(struct dlm_ls *ls, struct dlm_message *ms) lkb->lkb_remid = ms->m_lkid; if (is_altmode(lkb)) munge_altmode(lkb, ms); - if (result) + if (result) { add_lkb(r, lkb, DLM_LKSTS_WAITING); - else { + add_timeout(lkb); + } else { grant_lock_pc(r, lkb, ms); queue_cast(r, lkb, 0); } @@ -3172,6 +3383,12 @@ static void __receive_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, queue_cast(r, lkb, -EAGAIN); break; + case -EDEADLK: + receive_flags_reply(lkb, ms); + revert_lock_pc(r, lkb); + queue_cast(r, lkb, -EDEADLK); + break; + case -EINPROGRESS: /* convert was queued on remote master */ receive_flags_reply(lkb, ms); @@ -3179,6 +3396,7 @@ static void __receive_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, munge_demoted(lkb, ms); del_lkb(r, lkb); add_lkb(r, lkb, DLM_LKSTS_CONVERT); + add_timeout(lkb); break; case 0: @@ -3298,8 +3516,7 @@ static void _receive_cancel_reply(struct dlm_lkb *lkb, struct dlm_message *ms) case -DLM_ECANCEL: receive_flags_reply(lkb, ms); revert_lock_pc(r, lkb); - if (ms->m_result) - queue_cast(r, lkb, -DLM_ECANCEL); + queue_cast(r, lkb, -DLM_ECANCEL); break; case 0: break; @@ -3424,7 +3641,7 @@ int dlm_receive_message(struct dlm_header *hd, int nodeid, int recovery) } } - if (lock_recovery_try(ls)) + if (dlm_lock_recovery_try(ls)) break; schedule(); } @@ -3503,7 +3720,7 @@ int dlm_receive_message(struct dlm_header *hd, int nodeid, int recovery) log_error(ls, "unknown message type %d", ms->m_type); } - unlock_recovery(ls); + dlm_unlock_recovery(ls); out: dlm_put_lockspace(ls); dlm_astd_wake(); @@ -4034,13 +4251,13 @@ int dlm_recover_process_copy(struct dlm_ls *ls, struct dlm_rcom *rc) int dlm_user_request(struct dlm_ls *ls, struct dlm_user_args *ua, int mode, uint32_t flags, void *name, unsigned int namelen, - uint32_t parent_lkid) + unsigned long timeout_cs) { struct dlm_lkb *lkb; struct dlm_args args; int error; - lock_recovery(ls); + dlm_lock_recovery(ls); error = create_lkb(ls, &lkb); if (error) { @@ -4062,7 +4279,7 @@ int dlm_user_request(struct dlm_ls *ls, struct dlm_user_args *ua, When DLM_IFL_USER is set, the dlm knows that this is a userspace lock and that lkb_astparam is the dlm_user_args structure. */ - error = set_lock_args(mode, &ua->lksb, flags, namelen, parent_lkid, + error = set_lock_args(mode, &ua->lksb, flags, namelen, timeout_cs, DLM_FAKE_USER_AST, ua, DLM_FAKE_USER_AST, &args); lkb->lkb_flags |= DLM_IFL_USER; ua->old_mode = DLM_LOCK_IV; @@ -4094,19 +4311,20 @@ int dlm_user_request(struct dlm_ls *ls, struct dlm_user_args *ua, list_add_tail(&lkb->lkb_ownqueue, &ua->proc->locks); spin_unlock(&ua->proc->locks_spin); out: - unlock_recovery(ls); + dlm_unlock_recovery(ls); return error; } int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, - int mode, uint32_t flags, uint32_t lkid, char *lvb_in) + int mode, uint32_t flags, uint32_t lkid, char *lvb_in, + unsigned long timeout_cs) { struct dlm_lkb *lkb; struct dlm_args args; struct dlm_user_args *ua; int error; - lock_recovery(ls); + dlm_lock_recovery(ls); error = find_lkb(ls, lkid, &lkb); if (error) @@ -4127,6 +4345,7 @@ int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, if (lvb_in && ua->lksb.sb_lvbptr) memcpy(ua->lksb.sb_lvbptr, lvb_in, DLM_USER_LVB_LEN); + ua->xid = ua_tmp->xid; ua->castparam = ua_tmp->castparam; ua->castaddr = ua_tmp->castaddr; ua->bastparam = ua_tmp->bastparam; @@ -4134,19 +4353,19 @@ int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, ua->user_lksb = ua_tmp->user_lksb; ua->old_mode = lkb->lkb_grmode; - error = set_lock_args(mode, &ua->lksb, flags, 0, 0, DLM_FAKE_USER_AST, - ua, DLM_FAKE_USER_AST, &args); + error = set_lock_args(mode, &ua->lksb, flags, 0, timeout_cs, + DLM_FAKE_USER_AST, ua, DLM_FAKE_USER_AST, &args); if (error) goto out_put; error = convert_lock(ls, lkb, &args); - if (error == -EINPROGRESS || error == -EAGAIN) + if (error == -EINPROGRESS || error == -EAGAIN || error == -EDEADLK) error = 0; out_put: dlm_put_lkb(lkb); out: - unlock_recovery(ls); + dlm_unlock_recovery(ls); kfree(ua_tmp); return error; } @@ -4159,7 +4378,7 @@ int dlm_user_unlock(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, struct dlm_user_args *ua; int error; - lock_recovery(ls); + dlm_lock_recovery(ls); error = find_lkb(ls, lkid, &lkb); if (error) @@ -4194,7 +4413,7 @@ int dlm_user_unlock(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, out_put: dlm_put_lkb(lkb); out: - unlock_recovery(ls); + dlm_unlock_recovery(ls); kfree(ua_tmp); return error; } @@ -4207,7 +4426,7 @@ int dlm_user_cancel(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, struct dlm_user_args *ua; int error; - lock_recovery(ls); + dlm_lock_recovery(ls); error = find_lkb(ls, lkid, &lkb); if (error) @@ -4231,11 +4450,59 @@ int dlm_user_cancel(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, out_put: dlm_put_lkb(lkb); out: - unlock_recovery(ls); + dlm_unlock_recovery(ls); kfree(ua_tmp); return error; } +int dlm_user_deadlock(struct dlm_ls *ls, uint32_t flags, uint32_t lkid) +{ + struct dlm_lkb *lkb; + struct dlm_args args; + struct dlm_user_args *ua; + struct dlm_rsb *r; + int error; + + dlm_lock_recovery(ls); + + error = find_lkb(ls, lkid, &lkb); + if (error) + goto out; + + ua = (struct dlm_user_args *)lkb->lkb_astparam; + + error = set_unlock_args(flags, ua, &args); + if (error) + goto out_put; + + /* same as cancel_lock(), but set DEADLOCK_CANCEL after lock_rsb */ + + r = lkb->lkb_resource; + hold_rsb(r); + lock_rsb(r); + + error = validate_unlock_args(lkb, &args); + if (error) + goto out_r; + lkb->lkb_flags |= DLM_IFL_DEADLOCK_CANCEL; + + error = _cancel_lock(r, lkb); + out_r: + unlock_rsb(r); + put_rsb(r); + + if (error == -DLM_ECANCEL) + error = 0; + /* from validate_unlock_args() */ + if (error == -EBUSY) + error = 0; + out_put: + dlm_put_lkb(lkb); + out: + dlm_unlock_recovery(ls); + return error; +} + /* lkb's that are removed from the waiters list by revert are just left on the orphans list with the granted orphan locks, to be freed by purge */ @@ -4314,12 +4581,13 @@ void dlm_clear_proc_locks(struct dlm_ls *ls, struct dlm_user_proc *proc) { struct dlm_lkb *lkb, *safe; - lock_recovery(ls); + dlm_lock_recovery(ls); while (1) { lkb = del_proc_lock(ls, proc); if (!lkb) break; + del_timeout(lkb); if (lkb->lkb_exflags & DLM_LKF_PERSISTENT) orphan_proc_lock(ls, lkb); else @@ -4347,7 +4615,7 @@ void dlm_clear_proc_locks(struct dlm_ls *ls, struct dlm_user_proc *proc) } mutex_unlock(&ls->ls_clear_proc_locks); - unlock_recovery(ls); + dlm_unlock_recovery(ls); } static void purge_proc_locks(struct dlm_ls *ls, struct dlm_user_proc *proc) @@ -4429,12 +4697,12 @@ int dlm_user_purge(struct dlm_ls *ls, struct dlm_user_proc *proc, if (nodeid != dlm_our_nodeid()) { error = send_purge(ls, nodeid, pid); } else { - lock_recovery(ls); + dlm_lock_recovery(ls); if (pid == current->pid) purge_proc_locks(ls, proc); else do_purge(ls, nodeid, pid); - unlock_recovery(ls); + dlm_unlock_recovery(ls); } return error; } diff --git a/fs/dlm/lock.h b/fs/dlm/lock.h index 64fc4ec4066..1720313c22d 100644 --- a/fs/dlm/lock.h +++ b/fs/dlm/lock.h @@ -1,7 +1,7 @@ /****************************************************************************** ******************************************************************************* ** -** Copyright (C) 2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -24,6 +24,10 @@ void dlm_put_rsb(struct dlm_rsb *r); void dlm_hold_rsb(struct dlm_rsb *r); int dlm_put_lkb(struct dlm_lkb *lkb); void dlm_scan_rsbs(struct dlm_ls *ls); +int dlm_lock_recovery_try(struct dlm_ls *ls); +void dlm_unlock_recovery(struct dlm_ls *ls); +void dlm_scan_timeout(struct dlm_ls *ls); +void dlm_adjust_timeouts(struct dlm_ls *ls); int dlm_purge_locks(struct dlm_ls *ls); void dlm_purge_mstcpy_locks(struct dlm_rsb *r); @@ -34,15 +38,18 @@ int dlm_recover_master_copy(struct dlm_ls *ls, struct dlm_rcom *rc); int dlm_recover_process_copy(struct dlm_ls *ls, struct dlm_rcom *rc); int dlm_user_request(struct dlm_ls *ls, struct dlm_user_args *ua, int mode, - uint32_t flags, void *name, unsigned int namelen, uint32_t parent_lkid); + uint32_t flags, void *name, unsigned int namelen, + unsigned long timeout_cs); int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, - int mode, uint32_t flags, uint32_t lkid, char *lvb_in); + int mode, uint32_t flags, uint32_t lkid, char *lvb_in, + unsigned long timeout_cs); int dlm_user_unlock(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, uint32_t flags, uint32_t lkid, char *lvb_in); int dlm_user_cancel(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, uint32_t flags, uint32_t lkid); int dlm_user_purge(struct dlm_ls *ls, struct dlm_user_proc *proc, int nodeid, int pid); +int dlm_user_deadlock(struct dlm_ls *ls, uint32_t flags, uint32_t lkid); void dlm_clear_proc_locks(struct dlm_ls *ls, struct dlm_user_proc *proc); static inline int is_master(struct dlm_rsb *r) diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c index a677b2a5eed..1dc72105ab1 100644 --- a/fs/dlm/lockspace.c +++ b/fs/dlm/lockspace.c @@ -197,13 +197,24 @@ static int do_uevent(struct dlm_ls *ls, int in) else kobject_uevent(&ls->ls_kobj, KOBJ_OFFLINE); + log_debug(ls, "%s the lockspace group...", in ? "joining" : "leaving"); + + /* dlm_controld will see the uevent, do the necessary group management + and then write to sysfs to wake us */ + error = wait_event_interruptible(ls->ls_uevent_wait, test_and_clear_bit(LSFL_UEVENT_WAIT, &ls->ls_flags)); + + log_debug(ls, "group event done %d %d", error, ls->ls_uevent_result); + if (error) goto out; error = ls->ls_uevent_result; out: + if (error) + log_error(ls, "group %s failed %d %d", in ? "join" : "leave", + error, ls->ls_uevent_result); return error; } @@ -234,8 +245,13 @@ static int dlm_scand(void *data) struct dlm_ls *ls; while (!kthread_should_stop()) { - list_for_each_entry(ls, &lslist, ls_list) - dlm_scan_rsbs(ls); + list_for_each_entry(ls, &lslist, ls_list) { + if (dlm_lock_recovery_try(ls)) { + dlm_scan_rsbs(ls); + dlm_scan_timeout(ls); + dlm_unlock_recovery(ls); + } + } schedule_timeout_interruptible(dlm_config.ci_scan_secs * HZ); } return 0; @@ -395,6 +411,7 @@ static int new_lockspace(char *name, int namelen, void **lockspace, { struct dlm_ls *ls; int i, size, error = -ENOMEM; + int do_unreg = 0; if (namelen > DLM_LOCKSPACE_LEN) return -EINVAL; @@ -417,11 +434,22 @@ static int new_lockspace(char *name, int namelen, void **lockspace, goto out; memcpy(ls->ls_name, name, namelen); ls->ls_namelen = namelen; - ls->ls_exflags = flags; ls->ls_lvblen = lvblen; ls->ls_count = 0; ls->ls_flags = 0; + if (flags & DLM_LSFL_TIMEWARN) + set_bit(LSFL_TIMEWARN, &ls->ls_flags); + + if (flags & DLM_LSFL_FS) + ls->ls_allocation = GFP_NOFS; + else + ls->ls_allocation = GFP_KERNEL; + + /* ls_exflags are forced to match among nodes, and we don't + need to require all nodes to have TIMEWARN or FS set */ + ls->ls_exflags = (flags & ~(DLM_LSFL_TIMEWARN | DLM_LSFL_FS)); + size = dlm_config.ci_rsbtbl_size; ls->ls_rsbtbl_size = size; @@ -461,6 +489,8 @@ static int new_lockspace(char *name, int namelen, void **lockspace, mutex_init(&ls->ls_waiters_mutex); INIT_LIST_HEAD(&ls->ls_orphans); mutex_init(&ls->ls_orphans_mutex); + INIT_LIST_HEAD(&ls->ls_timeout); + mutex_init(&ls->ls_timeout_mutex); INIT_LIST_HEAD(&ls->ls_nodes); INIT_LIST_HEAD(&ls->ls_nodes_gone); @@ -477,6 +507,8 @@ static int new_lockspace(char *name, int namelen, void **lockspace, init_waitqueue_head(&ls->ls_uevent_wait); ls->ls_uevent_result = 0; + init_completion(&ls->ls_members_done); + ls->ls_members_result = -1; ls->ls_recoverd_task = NULL; mutex_init(&ls->ls_recoverd_active); @@ -513,32 +545,49 @@ static int new_lockspace(char *name, int namelen, void **lockspace, error = dlm_recoverd_start(ls); if (error) { log_error(ls, "can't start dlm_recoverd %d", error); - goto out_rcomfree; + goto out_delist; } - dlm_create_debug_file(ls); - error = kobject_setup(ls); if (error) - goto out_del; + goto out_stop; error = kobject_register(&ls->ls_kobj); if (error) - goto out_del; + goto out_stop; + + /* let kobject handle freeing of ls if there's an error */ + do_unreg = 1; + + /* This uevent triggers dlm_controld in userspace to add us to the + group of nodes that are members of this lockspace (managed by the + cluster infrastructure.) Once it's done that, it tells us who the + current lockspace members are (via configfs) and then tells the + lockspace to start running (via sysfs) in dlm_ls_start(). */ error = do_uevent(ls, 1); if (error) - goto out_unreg; + goto out_stop; + + wait_for_completion(&ls->ls_members_done); + error = ls->ls_members_result; + if (error) + goto out_members; + + dlm_create_debug_file(ls); + + log_debug(ls, "join complete"); *lockspace = ls; return 0; - out_unreg: - kobject_unregister(&ls->ls_kobj); - out_del: - dlm_delete_debug_file(ls); + out_members: + do_uevent(ls, 0); + dlm_clear_members(ls); + kfree(ls->ls_node_array); + out_stop: dlm_recoverd_stop(ls); - out_rcomfree: + out_delist: spin_lock(&lslist_lock); list_del(&ls->ls_list); spin_unlock(&lslist_lock); @@ -550,7 +599,10 @@ static int new_lockspace(char *name, int namelen, void **lockspace, out_rsbfree: kfree(ls->ls_rsbtbl); out_lsfree: - kfree(ls); + if (do_unreg) + kobject_unregister(&ls->ls_kobj); + else + kfree(ls); out: module_put(THIS_MODULE); return error; @@ -570,6 +622,8 @@ int dlm_new_lockspace(char *name, int namelen, void **lockspace, error = new_lockspace(name, namelen, lockspace, flags, lvblen); if (!error) ls_count++; + else if (!ls_count) + threads_stop(); out: mutex_unlock(&ls_lock); return error; @@ -696,7 +750,7 @@ static int release_lockspace(struct dlm_ls *ls, int force) dlm_clear_members_gone(ls); kfree(ls->ls_node_array); kobject_unregister(&ls->ls_kobj); - /* The ls structure will be freed when the kobject is done with */ + /* The ls structure will be freed when the kobject is done with */ mutex_lock(&ls_lock); ls_count--; diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index 27970a58d29..0553a6158dc 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c @@ -260,7 +260,7 @@ static int nodeid_to_addr(int nodeid, struct sockaddr *retaddr) static void lowcomms_data_ready(struct sock *sk, int count_unused) { struct connection *con = sock2con(sk); - if (!test_and_set_bit(CF_READ_PENDING, &con->flags)) + if (con && !test_and_set_bit(CF_READ_PENDING, &con->flags)) queue_work(recv_workqueue, &con->rwork); } @@ -268,7 +268,7 @@ static void lowcomms_write_space(struct sock *sk) { struct connection *con = sock2con(sk); - if (!test_and_set_bit(CF_WRITE_PENDING, &con->flags)) + if (con && !test_and_set_bit(CF_WRITE_PENDING, &con->flags)) queue_work(send_workqueue, &con->swork); } @@ -720,11 +720,17 @@ static int tcp_accept_from_sock(struct connection *con) INIT_WORK(&othercon->rwork, process_recv_sockets); set_bit(CF_IS_OTHERCON, &othercon->flags); newcon->othercon = othercon; + othercon->sock = newsock; + newsock->sk->sk_user_data = othercon; + add_sock(newsock, othercon); + addcon = othercon; + } + else { + printk("Extra connection from node %d attempted\n", nodeid); + result = -EAGAIN; + mutex_unlock(&newcon->sock_mutex); + goto accept_err; } - othercon->sock = newsock; - newsock->sk->sk_user_data = othercon; - add_sock(newsock, othercon); - addcon = othercon; } else { newsock->sk->sk_user_data = newcon; @@ -1400,8 +1406,11 @@ void dlm_lowcomms_stop(void) down(&connections_lock); for (i = 0; i <= max_nodeid; i++) { con = __nodeid2con(i, 0); - if (con) + if (con) { con->flags |= 0xFF; + if (con->sock) + con->sock->sk->sk_user_data = NULL; + } } up(&connections_lock); diff --git a/fs/dlm/main.c b/fs/dlm/main.c index 162fbae58fe..eca2907f238 100644 --- a/fs/dlm/main.c +++ b/fs/dlm/main.c @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -25,6 +25,8 @@ void dlm_unregister_debugfs(void); static inline int dlm_register_debugfs(void) { return 0; } static inline void dlm_unregister_debugfs(void) { } #endif +int dlm_netlink_init(void); +void dlm_netlink_exit(void); static int __init init_dlm(void) { @@ -50,10 +52,16 @@ static int __init init_dlm(void) if (error) goto out_debug; + error = dlm_netlink_init(); + if (error) + goto out_user; + printk("DLM (built %s %s) installed\n", __DATE__, __TIME__); return 0; + out_user: + dlm_user_exit(); out_debug: dlm_unregister_debugfs(); out_config: @@ -68,6 +76,7 @@ static int __init init_dlm(void) static void __exit exit_dlm(void) { + dlm_netlink_exit(); dlm_user_exit(); dlm_config_exit(); dlm_memory_exit(); diff --git a/fs/dlm/member.c b/fs/dlm/member.c index 85e2897bd74..073599dced2 100644 --- a/fs/dlm/member.c +++ b/fs/dlm/member.c @@ -1,7 +1,7 @@ /****************************************************************************** ******************************************************************************* ** -** Copyright (C) 2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -233,6 +233,12 @@ int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out) *neg_out = neg; error = ping_members(ls); + if (!error || error == -EPROTO) { + /* new_lockspace() may be waiting to know if the config + is good or bad */ + ls->ls_members_result = error; + complete(&ls->ls_members_done); + } if (error) goto out; @@ -284,6 +290,9 @@ int dlm_ls_stop(struct dlm_ls *ls) dlm_recoverd_suspend(ls); ls->ls_recover_status = 0; dlm_recoverd_resume(ls); + + if (!ls->ls_recover_begin) + ls->ls_recover_begin = jiffies; return 0; } diff --git a/fs/dlm/netlink.c b/fs/dlm/netlink.c new file mode 100644 index 00000000000..863b87d0dc7 --- /dev/null +++ b/fs/dlm/netlink.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2007 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + */ + +#include <net/genetlink.h> +#include <linux/dlm.h> +#include <linux/dlm_netlink.h> + +#include "dlm_internal.h" + +static uint32_t dlm_nl_seqnum; +static uint32_t listener_nlpid; + +static struct genl_family family = { + .id = GENL_ID_GENERATE, + .name = DLM_GENL_NAME, + .version = DLM_GENL_VERSION, +}; + +static int prepare_data(u8 cmd, struct sk_buff **skbp, size_t size) +{ + struct sk_buff *skb; + void *data; + + skb = genlmsg_new(size, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + /* add the message headers */ + data = genlmsg_put(skb, 0, dlm_nl_seqnum++, &family, 0, cmd); + if (!data) { + nlmsg_free(skb); + return -EINVAL; + } + + *skbp = skb; + return 0; +} + +static struct dlm_lock_data *mk_data(struct sk_buff *skb) +{ + struct nlattr *ret; + + ret = nla_reserve(skb, DLM_TYPE_LOCK, sizeof(struct dlm_lock_data)); + if (!ret) + return NULL; + return nla_data(ret); +} + +static int send_data(struct sk_buff *skb) +{ + struct genlmsghdr *genlhdr = nlmsg_data((struct nlmsghdr *)skb->data); + void *data = genlmsg_data(genlhdr); + int rv; + + rv = genlmsg_end(skb, data); + if (rv < 0) { + nlmsg_free(skb); + return rv; + } + + return genlmsg_unicast(skb, listener_nlpid); +} + +static int user_cmd(struct sk_buff *skb, struct genl_info *info) +{ + listener_nlpid = info->snd_pid; + printk("user_cmd nlpid %u\n", listener_nlpid); + return 0; +} + +static struct genl_ops dlm_nl_ops = { + .cmd = DLM_CMD_HELLO, + .doit = user_cmd, +}; + +int dlm_netlink_init(void) +{ + int rv; + + rv = genl_register_family(&family); + if (rv) + return rv; + + rv = genl_register_ops(&family, &dlm_nl_ops); + if (rv < 0) + goto err; + return 0; + err: + genl_unregister_family(&family); + return rv; +} + +void dlm_netlink_exit(void) +{ + genl_unregister_ops(&family, &dlm_nl_ops); + genl_unregister_family(&family); +} + +static void fill_data(struct dlm_lock_data *data, struct dlm_lkb *lkb) +{ + struct dlm_rsb *r = lkb->lkb_resource; + struct dlm_user_args *ua = (struct dlm_user_args *) lkb->lkb_astparam; + + memset(data, 0, sizeof(struct dlm_lock_data)); + + data->version = DLM_LOCK_DATA_VERSION; + data->nodeid = lkb->lkb_nodeid; + data->ownpid = lkb->lkb_ownpid; + data->id = lkb->lkb_id; + data->remid = lkb->lkb_remid; + data->status = lkb->lkb_status; + data->grmode = lkb->lkb_grmode; + data->rqmode = lkb->lkb_rqmode; + data->timestamp = lkb->lkb_timestamp; + if (ua) + data->xid = ua->xid; + if (r) { + data->lockspace_id = r->res_ls->ls_global_id; + data->resource_namelen = r->res_length; + memcpy(data->resource_name, r->res_name, r->res_length); + } +} + +void dlm_timeout_warn(struct dlm_lkb *lkb) +{ + struct dlm_lock_data *data; + struct sk_buff *send_skb; + size_t size; + int rv; + + size = nla_total_size(sizeof(struct dlm_lock_data)) + + nla_total_size(0); /* why this? */ + + rv = prepare_data(DLM_CMD_TIMEOUT, &send_skb, size); + if (rv < 0) + return; + + data = mk_data(send_skb); + if (!data) { + nlmsg_free(send_skb); + return; + } + + fill_data(data, lkb); + + send_data(send_skb); +} + diff --git a/fs/dlm/rcom.c b/fs/dlm/rcom.c index 6bfbd615380..e3a1527cbdb 100644 --- a/fs/dlm/rcom.c +++ b/fs/dlm/rcom.c @@ -38,7 +38,7 @@ static int create_rcom(struct dlm_ls *ls, int to_nodeid, int type, int len, char *mb; int mb_len = sizeof(struct dlm_rcom) + len; - mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, GFP_KERNEL, &mb); + mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, ls->ls_allocation, &mb); if (!mh) { log_print("create_rcom to %d type %d len %d ENOBUFS", to_nodeid, type, len); @@ -90,7 +90,7 @@ static int check_config(struct dlm_ls *ls, struct dlm_rcom *rc, int nodeid) log_error(ls, "version mismatch: %x nodeid %d: %x", DLM_HEADER_MAJOR | DLM_HEADER_MINOR, nodeid, rc->rc_header.h_version); - return -EINVAL; + return -EPROTO; } if (rf->rf_lvblen != ls->ls_lvblen || @@ -98,7 +98,7 @@ static int check_config(struct dlm_ls *ls, struct dlm_rcom *rc, int nodeid) log_error(ls, "config mismatch: %d,%x nodeid %d: %d,%x", ls->ls_lvblen, ls->ls_exflags, nodeid, rf->rf_lvblen, rf->rf_lsflags); - return -EINVAL; + return -EPROTO; } return 0; } @@ -386,7 +386,8 @@ static void receive_rcom_lock_reply(struct dlm_ls *ls, struct dlm_rcom *rc_in) dlm_recover_process_copy(ls, rc_in); } -static int send_ls_not_ready(int nodeid, struct dlm_rcom *rc_in) +static int send_ls_not_ready(struct dlm_ls *ls, int nodeid, + struct dlm_rcom *rc_in) { struct dlm_rcom *rc; struct rcom_config *rf; @@ -394,7 +395,7 @@ static int send_ls_not_ready(int nodeid, struct dlm_rcom *rc_in) char *mb; int mb_len = sizeof(struct dlm_rcom) + sizeof(struct rcom_config); - mh = dlm_lowcomms_get_buffer(nodeid, mb_len, GFP_KERNEL, &mb); + mh = dlm_lowcomms_get_buffer(nodeid, mb_len, ls->ls_allocation, &mb); if (!mh) return -ENOBUFS; memset(mb, 0, mb_len); @@ -464,7 +465,7 @@ void dlm_receive_rcom(struct dlm_header *hd, int nodeid) log_print("lockspace %x from %d type %x not found", hd->h_lockspace, nodeid, rc->rc_type); if (rc->rc_type == DLM_RCOM_STATUS) - send_ls_not_ready(nodeid, rc); + send_ls_not_ready(ls, nodeid, rc); return; } diff --git a/fs/dlm/recoverd.c b/fs/dlm/recoverd.c index 3cb636d6024..66575997861 100644 --- a/fs/dlm/recoverd.c +++ b/fs/dlm/recoverd.c @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -190,6 +190,8 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv) dlm_clear_members_gone(ls); + dlm_adjust_timeouts(ls); + error = enable_locking(ls, rv->seq); if (error) { log_debug(ls, "enable_locking failed %d", error); diff --git a/fs/dlm/user.c b/fs/dlm/user.c index b0201ec325a..6438941ab1f 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c @@ -33,16 +33,17 @@ static const struct file_operations device_fops; struct dlm_lock_params32 { __u8 mode; __u8 namelen; - __u16 flags; + __u16 unused; + __u32 flags; __u32 lkid; __u32 parent; - + __u64 xid; + __u64 timeout; __u32 castparam; __u32 castaddr; __u32 bastparam; __u32 bastaddr; __u32 lksb; - char lvb[DLM_USER_LVB_LEN]; char name[0]; }; @@ -68,6 +69,7 @@ struct dlm_lksb32 { }; struct dlm_lock_result32 { + __u32 version[3]; __u32 length; __u32 user_astaddr; __u32 user_astparam; @@ -102,6 +104,8 @@ static void compat_input(struct dlm_write_request *kb, kb->i.lock.flags = kb32->i.lock.flags; kb->i.lock.lkid = kb32->i.lock.lkid; kb->i.lock.parent = kb32->i.lock.parent; + kb->i.lock.xid = kb32->i.lock.xid; + kb->i.lock.timeout = kb32->i.lock.timeout; kb->i.lock.castparam = (void *)(long)kb32->i.lock.castparam; kb->i.lock.castaddr = (void *)(long)kb32->i.lock.castaddr; kb->i.lock.bastparam = (void *)(long)kb32->i.lock.bastparam; @@ -115,6 +119,10 @@ static void compat_input(struct dlm_write_request *kb, static void compat_output(struct dlm_lock_result *res, struct dlm_lock_result32 *res32) { + res32->version[0] = res->version[0]; + res32->version[1] = res->version[1]; + res32->version[2] = res->version[2]; + res32->user_astaddr = (__u32)(long)res->user_astaddr; res32->user_astparam = (__u32)(long)res->user_astparam; res32->user_lksb = (__u32)(long)res->user_lksb; @@ -130,6 +138,36 @@ static void compat_output(struct dlm_lock_result *res, } #endif +/* Figure out if this lock is at the end of its life and no longer + available for the application to use. The lkb still exists until + the final ast is read. A lock becomes EOL in three situations: + 1. a noqueue request fails with EAGAIN + 2. an unlock completes with EUNLOCK + 3. a cancel of a waiting request completes with ECANCEL/EDEADLK + An EOL lock needs to be removed from the process's list of locks. + And we can't allow any new operation on an EOL lock. This is + not related to the lifetime of the lkb struct which is managed + entirely by refcount. */ + +static int lkb_is_endoflife(struct dlm_lkb *lkb, int sb_status, int type) +{ + switch (sb_status) { + case -DLM_EUNLOCK: + return 1; + case -DLM_ECANCEL: + case -ETIMEDOUT: + case -EDEADLK: + if (lkb->lkb_grmode == DLM_LOCK_IV) + return 1; + break; + case -EAGAIN: + if (type == AST_COMP && lkb->lkb_grmode == DLM_LOCK_IV) + return 1; + break; + } + return 0; +} + /* we could possibly check if the cancel of an orphan has resulted in the lkb being removed and then remove that lkb from the orphans list and free it */ @@ -176,25 +214,7 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type) log_debug(ls, "ast overlap %x status %x %x", lkb->lkb_id, ua->lksb.sb_status, lkb->lkb_flags); - /* Figure out if this lock is at the end of its life and no longer - available for the application to use. The lkb still exists until - the final ast is read. A lock becomes EOL in three situations: - 1. a noqueue request fails with EAGAIN - 2. an unlock completes with EUNLOCK - 3. a cancel of a waiting request completes with ECANCEL - An EOL lock needs to be removed from the process's list of locks. - And we can't allow any new operation on an EOL lock. This is - not related to the lifetime of the lkb struct which is managed - entirely by refcount. */ - - if (type == AST_COMP && - lkb->lkb_grmode == DLM_LOCK_IV && - ua->lksb.sb_status == -EAGAIN) - eol = 1; - else if (ua->lksb.sb_status == -DLM_EUNLOCK || - (ua->lksb.sb_status == -DLM_ECANCEL && - lkb->lkb_grmode == DLM_LOCK_IV)) - eol = 1; + eol = lkb_is_endoflife(lkb, ua->lksb.sb_status, type); if (eol) { lkb->lkb_ast_type &= ~AST_BAST; lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; @@ -252,16 +272,18 @@ static int device_user_lock(struct dlm_user_proc *proc, ua->castaddr = params->castaddr; ua->bastparam = params->bastparam; ua->bastaddr = params->bastaddr; + ua->xid = params->xid; if (params->flags & DLM_LKF_CONVERT) error = dlm_user_convert(ls, ua, params->mode, params->flags, - params->lkid, params->lvb); + params->lkid, params->lvb, + (unsigned long) params->timeout); else { error = dlm_user_request(ls, ua, params->mode, params->flags, params->name, params->namelen, - params->parent); + (unsigned long) params->timeout); if (!error) error = ua->lksb.sb_lkid; } @@ -299,6 +321,22 @@ static int device_user_unlock(struct dlm_user_proc *proc, return error; } +static int device_user_deadlock(struct dlm_user_proc *proc, + struct dlm_lock_params *params) +{ + struct dlm_ls *ls; + int error; + + ls = dlm_find_lockspace_local(proc->lockspace); + if (!ls) + return -ENOENT; + + error = dlm_user_deadlock(ls, params->flags, params->lkid); + + dlm_put_lockspace(ls); + return error; +} + static int create_misc_device(struct dlm_ls *ls, char *name) { int error, len; @@ -348,7 +386,7 @@ static int device_create_lockspace(struct dlm_lspace_params *params) return -EPERM; error = dlm_new_lockspace(params->name, strlen(params->name), - &lockspace, 0, DLM_USER_LVB_LEN); + &lockspace, params->flags, DLM_USER_LVB_LEN); if (error) return error; @@ -524,6 +562,14 @@ static ssize_t device_write(struct file *file, const char __user *buf, error = device_user_unlock(proc, &kbuf->i.lock); break; + case DLM_USER_DEADLOCK: + if (!proc) { + log_print("no locking on control device"); + goto out_sig; + } + error = device_user_deadlock(proc, &kbuf->i.lock); + break; + case DLM_USER_CREATE_LOCKSPACE: if (proc) { log_print("create/remove only on control device"); @@ -641,6 +687,9 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, int struct_len; memset(&result, 0, sizeof(struct dlm_lock_result)); + result.version[0] = DLM_DEVICE_VERSION_MAJOR; + result.version[1] = DLM_DEVICE_VERSION_MINOR; + result.version[2] = DLM_DEVICE_VERSION_PATCH; memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb)); result.user_lksb = ua->user_lksb; @@ -699,6 +748,20 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, return error; } +static int copy_version_to_user(char __user *buf, size_t count) +{ + struct dlm_device_version ver; + + memset(&ver, 0, sizeof(struct dlm_device_version)); + ver.version[0] = DLM_DEVICE_VERSION_MAJOR; + ver.version[1] = DLM_DEVICE_VERSION_MINOR; + ver.version[2] = DLM_DEVICE_VERSION_PATCH; + + if (copy_to_user(buf, &ver, sizeof(struct dlm_device_version))) + return -EFAULT; + return sizeof(struct dlm_device_version); +} + /* a read returns a single ast described in a struct dlm_lock_result */ static ssize_t device_read(struct file *file, char __user *buf, size_t count, @@ -710,6 +773,16 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, DECLARE_WAITQUEUE(wait, current); int error, type=0, bmode=0, removed = 0; + if (count == sizeof(struct dlm_device_version)) { + error = copy_version_to_user(buf, count); + return error; + } + + if (!proc) { + log_print("non-version read from control device %zu", count); + return -EINVAL; + } + #ifdef CONFIG_COMPAT if (count < sizeof(struct dlm_lock_result32)) #else @@ -747,11 +820,6 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, } } - if (list_empty(&proc->asts)) { - spin_unlock(&proc->asts_spin); - return -EAGAIN; - } - /* there may be both completion and blocking asts to return for the lkb, don't remove lkb from asts list unless no asts remain */ @@ -823,6 +891,7 @@ static const struct file_operations device_fops = { static const struct file_operations ctl_device_fops = { .open = ctl_device_open, .release = ctl_device_close, + .read = device_read, .write = device_write, .owner = THIS_MODULE, }; diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 59288d81707..94f456fe4d9 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -338,16 +338,17 @@ static int ecryptfs_fasync(int fd, struct file *file, int flag) return rc; } -static ssize_t ecryptfs_sendfile(struct file *file, loff_t * ppos, - size_t count, read_actor_t actor, void *target) +static ssize_t ecryptfs_splice_read(struct file *file, loff_t * ppos, + struct pipe_inode_info *pipe, size_t count, + unsigned int flags) { struct file *lower_file = NULL; int rc = -EINVAL; lower_file = ecryptfs_file_to_lower(file); - if (lower_file->f_op && lower_file->f_op->sendfile) - rc = lower_file->f_op->sendfile(lower_file, ppos, count, - actor, target); + if (lower_file->f_op && lower_file->f_op->splice_read) + rc = lower_file->f_op->splice_read(lower_file, ppos, pipe, + count, flags); return rc; } @@ -364,7 +365,7 @@ const struct file_operations ecryptfs_dir_fops = { .release = ecryptfs_release, .fsync = ecryptfs_fsync, .fasync = ecryptfs_fasync, - .sendfile = ecryptfs_sendfile, + .splice_read = ecryptfs_splice_read, }; const struct file_operations ecryptfs_main_fops = { @@ -381,7 +382,7 @@ const struct file_operations ecryptfs_main_fops = { .release = ecryptfs_release, .fsync = ecryptfs_fsync, .fasync = ecryptfs_fasync, - .sendfile = ecryptfs_sendfile, + .splice_read = ecryptfs_splice_read, }; static int diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 566d4e2d385..04afeecaaef 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -53,7 +53,6 @@ const struct file_operations ext2_file_operations = { .open = generic_file_open, .release = ext2_release_file, .fsync = ext2_sync_file, - .sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; @@ -71,7 +70,6 @@ const struct file_operations ext2_xip_file_operations = { .open = generic_file_open, .release = ext2_release_file, .fsync = ext2_sync_file, - .sendfile = xip_file_sendfile, }; #endif diff --git a/fs/ext3/file.c b/fs/ext3/file.c index 1e6f1386453..acc4913d301 100644 --- a/fs/ext3/file.c +++ b/fs/ext3/file.c @@ -120,7 +120,6 @@ const struct file_operations ext3_file_operations = { .open = generic_file_open, .release = ext3_release_file, .fsync = ext3_sync_file, - .sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 3c6c1fd2be9..d4c8186aed6 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -120,7 +120,6 @@ const struct file_operations ext4_file_operations = { .open = generic_file_open, .release = ext4_release_file, .fsync = ext4_sync_file, - .sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; diff --git a/fs/fat/file.c b/fs/fat/file.c index 55d3c7461c5..69a83b59dce 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -134,7 +134,7 @@ const struct file_operations fat_file_operations = { .release = fat_file_release, .ioctl = fat_generic_ioctl, .fsync = file_fsync, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; static int fat_cont_expand(struct inode *inode, loff_t size) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index adf7995232b..f79de7c8cdf 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -802,7 +802,7 @@ static const struct file_operations fuse_file_operations = { .release = fuse_release, .fsync = fuse_fsync, .lock = fuse_file_lock, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; static const struct file_operations fuse_direct_io_file_operations = { @@ -814,7 +814,7 @@ static const struct file_operations fuse_direct_io_file_operations = { .release = fuse_release, .fsync = fuse_fsync, .lock = fuse_file_lock, - /* no mmap and sendfile */ + /* no mmap and splice_read */ }; static const struct address_space_operations fuse_file_aops = { diff --git a/fs/gfs2/Makefile b/fs/gfs2/Makefile index e3f1ada643a..04ad0caebed 100644 --- a/fs/gfs2/Makefile +++ b/fs/gfs2/Makefile @@ -1,7 +1,7 @@ obj-$(CONFIG_GFS2_FS) += gfs2.o gfs2-y := acl.o bmap.o daemon.o dir.o eaops.o eattr.o glock.o \ glops.o inode.o lm.o log.o lops.o locking.o main.o meta_io.o \ - mount.o ondisk.o ops_address.o ops_dentry.o ops_export.o ops_file.o \ + mount.o ops_address.o ops_dentry.o ops_export.o ops_file.o \ ops_fstype.o ops_inode.o ops_super.o ops_vm.o quota.o \ recovery.o rgrp.o super.o sys.o trans.o util.o diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index c53a5d2d059..cd805a66880 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -718,7 +718,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, for (x = 0; x < rlist.rl_rgrps; x++) { struct gfs2_rgrpd *rgd; rgd = rlist.rl_ghs[x].gh_gl->gl_object; - rg_blocks += rgd->rd_ri.ri_length; + rg_blocks += rgd->rd_length; } error = gfs2_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs); @@ -772,7 +772,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, gfs2_free_data(ip, bstart, blen); } - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(ip, dibh->b_data); @@ -824,7 +824,7 @@ static int do_grow(struct gfs2_inode *ip, u64 size) goto out_gunlock_q; error = gfs2_trans_begin(sdp, - sdp->sd_max_height + al->al_rgd->rd_ri.ri_length + + sdp->sd_max_height + al->al_rgd->rd_length + RES_JDATA + RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) goto out_ipres; @@ -847,7 +847,7 @@ static int do_grow(struct gfs2_inode *ip, u64 size) } ip->i_di.di_size = size; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; error = gfs2_meta_inode_buffer(ip, &dibh); if (error) @@ -885,7 +885,6 @@ static int gfs2_block_truncate_page(struct address_space *mapping) unsigned blocksize, iblock, length, pos; struct buffer_head *bh; struct page *page; - void *kaddr; int err; page = grab_cache_page(mapping, index); @@ -928,15 +927,13 @@ static int gfs2_block_truncate_page(struct address_space *mapping) /* Uhhuh. Read error. Complain and punt. */ if (!buffer_uptodate(bh)) goto unlock; + err = 0; } if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) gfs2_trans_add_bh(ip->i_gl, bh, 0); - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, length, KM_USER0); unlock: unlock_page(page); @@ -962,7 +959,7 @@ static int trunc_start(struct gfs2_inode *ip, u64 size) if (gfs2_is_stuffed(ip)) { ip->i_di.di_size = size; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + size); @@ -974,7 +971,7 @@ static int trunc_start(struct gfs2_inode *ip, u64 size) if (!error) { ip->i_di.di_size = size; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; ip->i_di.di_flags |= GFS2_DIF_TRUNC_IN_PROG; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); @@ -1044,10 +1041,10 @@ static int trunc_end(struct gfs2_inode *ip) ip->i_di.di_height = 0; ip->i_di.di_goal_meta = ip->i_di.di_goal_data = - ip->i_num.no_addr; + ip->i_no_addr; gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); } - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; ip->i_di.di_flags &= ~GFS2_DIF_TRUNC_IN_PROG; gfs2_trans_add_bh(ip->i_gl, dibh, 1); diff --git a/fs/gfs2/daemon.c b/fs/gfs2/daemon.c index 683cb5bda87..3548d9f31e0 100644 --- a/fs/gfs2/daemon.c +++ b/fs/gfs2/daemon.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/gfs2_ondisk.h> #include <linux/lm_interface.h> +#include <linux/freezer.h> #include "gfs2.h" #include "incore.h" @@ -49,6 +50,8 @@ int gfs2_scand(void *data) while (!kthread_should_stop()) { gfs2_scand_internal(sdp); t = gfs2_tune_get(sdp, gt_scand_secs) * HZ; + if (freezing(current)) + refrigerator(); schedule_timeout_interruptible(t); } @@ -74,6 +77,8 @@ int gfs2_glockd(void *data) wait_event_interruptible(sdp->sd_reclaim_wq, (atomic_read(&sdp->sd_reclaim_count) || kthread_should_stop())); + if (freezing(current)) + refrigerator(); } return 0; @@ -93,6 +98,8 @@ int gfs2_recoverd(void *data) while (!kthread_should_stop()) { gfs2_check_journals(sdp); t = gfs2_tune_get(sdp, gt_recoverd_secs) * HZ; + if (freezing(current)) + refrigerator(); schedule_timeout_interruptible(t); } @@ -141,6 +148,8 @@ int gfs2_logd(void *data) } t = gfs2_tune_get(sdp, gt_logd_secs) * HZ; + if (freezing(current)) + refrigerator(); schedule_timeout_interruptible(t); } @@ -191,6 +200,8 @@ int gfs2_quotad(void *data) gfs2_quota_scan(sdp); t = gfs2_tune_get(sdp, gt_quotad_secs) * HZ; + if (freezing(current)) + refrigerator(); schedule_timeout_interruptible(t); } diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index a96fa07b3f3..2beb2f401aa 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -130,7 +130,7 @@ static int gfs2_dir_write_stuffed(struct gfs2_inode *ip, const char *buf, memcpy(dibh->b_data + offset + sizeof(struct gfs2_dinode), buf, size); if (ip->i_di.di_size < offset + size) ip->i_di.di_size = offset + size; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); @@ -228,7 +228,7 @@ out: if (ip->i_di.di_size < offset + copied) ip->i_di.di_size = offset + copied; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); @@ -1456,7 +1456,7 @@ int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, if (dip->i_di.di_entries != g.offset) { fs_warn(sdp, "Number of entries corrupt in dir %llu, " "ip->i_di.di_entries (%u) != g.offset (%u)\n", - (unsigned long long)dip->i_num.no_addr, + (unsigned long long)dip->i_no_addr, dip->i_di.di_entries, g.offset); error = -EIO; @@ -1488,24 +1488,55 @@ out: * Returns: errno */ -int gfs2_dir_search(struct inode *dir, const struct qstr *name, - struct gfs2_inum_host *inum, unsigned int *type) +struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name) { struct buffer_head *bh; struct gfs2_dirent *dent; + struct inode *inode; + + dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh); + if (dent) { + if (IS_ERR(dent)) + return ERR_PTR(PTR_ERR(dent)); + inode = gfs2_inode_lookup(dir->i_sb, + be16_to_cpu(dent->de_type), + be64_to_cpu(dent->de_inum.no_addr), + be64_to_cpu(dent->de_inum.no_formal_ino)); + brelse(bh); + return inode; + } + return ERR_PTR(-ENOENT); +} + +int gfs2_dir_check(struct inode *dir, const struct qstr *name, + const struct gfs2_inode *ip) +{ + struct buffer_head *bh; + struct gfs2_dirent *dent; + int ret = -ENOENT; dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh); if (dent) { if (IS_ERR(dent)) return PTR_ERR(dent); - if (inum) - gfs2_inum_in(inum, (char *)&dent->de_inum); - if (type) - *type = be16_to_cpu(dent->de_type); + if (ip) { + if (be64_to_cpu(dent->de_inum.no_addr) != ip->i_no_addr) + goto out; + if (be64_to_cpu(dent->de_inum.no_formal_ino) != + ip->i_no_formal_ino) + goto out; + if (unlikely(IF2DT(ip->i_inode.i_mode) != + be16_to_cpu(dent->de_type))) { + gfs2_consist_inode(GFS2_I(dir)); + ret = -EIO; + goto out; + } + } + ret = 0; +out: brelse(bh); - return 0; } - return -ENOENT; + return ret; } static int dir_new_leaf(struct inode *inode, const struct qstr *name) @@ -1565,7 +1596,7 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name) */ int gfs2_dir_add(struct inode *inode, const struct qstr *name, - const struct gfs2_inum_host *inum, unsigned type) + const struct gfs2_inode *nip, unsigned type) { struct gfs2_inode *ip = GFS2_I(inode); struct buffer_head *bh; @@ -1580,7 +1611,7 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name, if (IS_ERR(dent)) return PTR_ERR(dent); dent = gfs2_init_dirent(inode, dent, name, bh); - gfs2_inum_out(inum, (char *)&dent->de_inum); + gfs2_inum_out(nip, dent); dent->de_type = cpu_to_be16(type); if (ip->i_di.di_flags & GFS2_DIF_EXHASH) { leaf = (struct gfs2_leaf *)bh->b_data; @@ -1592,7 +1623,7 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name, break; gfs2_trans_add_bh(ip->i_gl, bh, 1); ip->i_di.di_entries++; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(ip, bh->b_data); brelse(bh); error = 0; @@ -1678,7 +1709,7 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) gfs2_consist_inode(dip); gfs2_trans_add_bh(dip->i_gl, bh, 1); dip->i_di.di_entries--; - dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME_SEC; + dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(dip, bh->b_data); brelse(bh); mark_inode_dirty(&dip->i_inode); @@ -1700,7 +1731,7 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) */ int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, - struct gfs2_inum_host *inum, unsigned int new_type) + const struct gfs2_inode *nip, unsigned int new_type) { struct buffer_head *bh; struct gfs2_dirent *dent; @@ -1715,7 +1746,7 @@ int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, return PTR_ERR(dent); gfs2_trans_add_bh(dip->i_gl, bh, 1); - gfs2_inum_out(inum, (char *)&dent->de_inum); + gfs2_inum_out(nip, dent); dent->de_type = cpu_to_be16(new_type); if (dip->i_di.di_flags & GFS2_DIF_EXHASH) { @@ -1726,7 +1757,7 @@ int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, gfs2_trans_add_bh(dip->i_gl, bh, 1); } - dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME_SEC; + dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(dip, bh->b_data); brelse(bh); return 0; @@ -1867,7 +1898,7 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, for (x = 0; x < rlist.rl_rgrps; x++) { struct gfs2_rgrpd *rgd; rgd = rlist.rl_ghs[x].gh_gl->gl_object; - rg_blocks += rgd->rd_ri.ri_length; + rg_blocks += rgd->rd_length; } error = gfs2_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs); diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h index 48fe89046bb..8a468cac932 100644 --- a/fs/gfs2/dir.h +++ b/fs/gfs2/dir.h @@ -16,15 +16,16 @@ struct inode; struct gfs2_inode; struct gfs2_inum; -int gfs2_dir_search(struct inode *dir, const struct qstr *filename, - struct gfs2_inum_host *inum, unsigned int *type); +struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *filename); +int gfs2_dir_check(struct inode *dir, const struct qstr *filename, + const struct gfs2_inode *ip); int gfs2_dir_add(struct inode *inode, const struct qstr *filename, - const struct gfs2_inum_host *inum, unsigned int type); + const struct gfs2_inode *ip, unsigned int type); int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename); int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, filldir_t filldir); int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, - struct gfs2_inum_host *new_inum, unsigned int new_type); + const struct gfs2_inode *nip, unsigned int new_type); int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip); diff --git a/fs/gfs2/eattr.c b/fs/gfs2/eattr.c index 5b83ca6acab..2a7435b5c4d 100644 --- a/fs/gfs2/eattr.c +++ b/fs/gfs2/eattr.c @@ -254,7 +254,7 @@ static int ea_dealloc_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, if (error) return error; - error = gfs2_trans_begin(sdp, rgd->rd_ri.ri_length + RES_DINODE + + error = gfs2_trans_begin(sdp, rgd->rd_length + RES_DINODE + RES_EATTR + RES_STATFS + RES_QUOTA, blks); if (error) goto out_gunlock; @@ -300,7 +300,7 @@ static int ea_dealloc_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, error = gfs2_meta_inode_buffer(ip, &dibh); if (!error) { - ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); @@ -700,7 +700,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er, goto out_gunlock_q; error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), - blks + al->al_rgd->rd_ri.ri_length + + blks + al->al_rgd->rd_length + RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) goto out_ipres; @@ -717,7 +717,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er, (er->er_mode & S_IFMT)); ip->i_inode.i_mode = er->er_mode; } - ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); @@ -852,7 +852,7 @@ static int ea_set_simple_noalloc(struct gfs2_inode *ip, struct buffer_head *bh, (ip->i_inode.i_mode & S_IFMT) == (er->er_mode & S_IFMT)); ip->i_inode.i_mode = er->er_mode; } - ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); @@ -1133,7 +1133,7 @@ static int ea_remove_stuffed(struct gfs2_inode *ip, struct gfs2_ea_location *el) error = gfs2_meta_inode_buffer(ip, &dibh); if (!error) { - ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); @@ -1352,7 +1352,7 @@ static int ea_dealloc_indirect(struct gfs2_inode *ip) for (x = 0; x < rlist.rl_rgrps; x++) { struct gfs2_rgrpd *rgd; rgd = rlist.rl_ghs[x].gh_gl->gl_object; - rg_blocks += rgd->rd_ri.ri_length; + rg_blocks += rgd->rd_length; } error = gfs2_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 1815429a297..3f0974e1afe 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -422,11 +422,11 @@ void gfs2_holder_uninit(struct gfs2_holder *gh) static void gfs2_holder_wake(struct gfs2_holder *gh) { clear_bit(HIF_WAIT, &gh->gh_iflags); - smp_mb(); + smp_mb__after_clear_bit(); wake_up_bit(&gh->gh_iflags, HIF_WAIT); } -static int holder_wait(void *word) +static int just_schedule(void *word) { schedule(); return 0; @@ -435,7 +435,20 @@ static int holder_wait(void *word) static void wait_on_holder(struct gfs2_holder *gh) { might_sleep(); - wait_on_bit(&gh->gh_iflags, HIF_WAIT, holder_wait, TASK_UNINTERRUPTIBLE); + wait_on_bit(&gh->gh_iflags, HIF_WAIT, just_schedule, TASK_UNINTERRUPTIBLE); +} + +static void gfs2_demote_wake(struct gfs2_glock *gl) +{ + clear_bit(GLF_DEMOTE, &gl->gl_flags); + smp_mb__after_clear_bit(); + wake_up_bit(&gl->gl_flags, GLF_DEMOTE); +} + +static void wait_on_demote(struct gfs2_glock *gl) +{ + might_sleep(); + wait_on_bit(&gl->gl_flags, GLF_DEMOTE, just_schedule, TASK_UNINTERRUPTIBLE); } /** @@ -528,7 +541,7 @@ static int rq_demote(struct gfs2_glock *gl) if (gl->gl_state == gl->gl_demote_state || gl->gl_state == LM_ST_UNLOCKED) { - clear_bit(GLF_DEMOTE, &gl->gl_flags); + gfs2_demote_wake(gl); return 0; } set_bit(GLF_LOCK, &gl->gl_flags); @@ -666,12 +679,22 @@ static void gfs2_glmutex_unlock(struct gfs2_glock *gl) * practise: LM_ST_SHARED and LM_ST_UNLOCKED */ -static void handle_callback(struct gfs2_glock *gl, unsigned int state) +static void handle_callback(struct gfs2_glock *gl, unsigned int state, int remote) { spin_lock(&gl->gl_spin); if (test_and_set_bit(GLF_DEMOTE, &gl->gl_flags) == 0) { gl->gl_demote_state = state; gl->gl_demote_time = jiffies; + if (remote && gl->gl_ops->go_type == LM_TYPE_IOPEN && + gl->gl_object) { + struct inode *inode = igrab(gl->gl_object); + spin_unlock(&gl->gl_spin); + if (inode) { + d_prune_aliases(inode); + iput(inode); + } + return; + } } else if (gl->gl_demote_state != LM_ST_UNLOCKED) { gl->gl_demote_state = state; } @@ -740,7 +763,7 @@ static void xmote_bh(struct gfs2_glock *gl, unsigned int ret) if (ret & LM_OUT_CANCELED) op_done = 0; else - clear_bit(GLF_DEMOTE, &gl->gl_flags); + gfs2_demote_wake(gl); } else { spin_lock(&gl->gl_spin); list_del_init(&gh->gh_list); @@ -848,7 +871,7 @@ static void drop_bh(struct gfs2_glock *gl, unsigned int ret) gfs2_assert_warn(sdp, !ret); state_change(gl, LM_ST_UNLOCKED); - clear_bit(GLF_DEMOTE, &gl->gl_flags); + gfs2_demote_wake(gl); if (glops->go_inval) glops->go_inval(gl, DIO_METADATA); @@ -1174,7 +1197,7 @@ void gfs2_glock_dq(struct gfs2_holder *gh) const struct gfs2_glock_operations *glops = gl->gl_ops; if (gh->gh_flags & GL_NOCACHE) - handle_callback(gl, LM_ST_UNLOCKED); + handle_callback(gl, LM_ST_UNLOCKED, 0); gfs2_glmutex_lock(gl); @@ -1196,6 +1219,13 @@ void gfs2_glock_dq(struct gfs2_holder *gh) spin_unlock(&gl->gl_spin); } +void gfs2_glock_dq_wait(struct gfs2_holder *gh) +{ + struct gfs2_glock *gl = gh->gh_gl; + gfs2_glock_dq(gh); + wait_on_demote(gl); +} + /** * gfs2_glock_dq_uninit - dequeue a holder from a glock and initialize it * @gh: the holder structure @@ -1297,10 +1327,6 @@ static int nq_m_sync(unsigned int num_gh, struct gfs2_holder *ghs, * @num_gh: the number of structures * @ghs: an array of struct gfs2_holder structures * - * Figure out how big an impact this function has. Either: - * 1) Replace this code with code that calls gfs2_glock_prefetch() - * 2) Forget async stuff and just call nq_m_sync() - * 3) Leave it like it is * * Returns: 0 on success (all glocks acquired), * errno on failure (no glocks acquired) @@ -1308,62 +1334,28 @@ static int nq_m_sync(unsigned int num_gh, struct gfs2_holder *ghs, int gfs2_glock_nq_m(unsigned int num_gh, struct gfs2_holder *ghs) { - int *e; - unsigned int x; - int borked = 0, serious = 0; + struct gfs2_holder *tmp[4]; + struct gfs2_holder **pph = tmp; int error = 0; - if (!num_gh) + switch(num_gh) { + case 0: return 0; - - if (num_gh == 1) { + case 1: ghs->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC); return gfs2_glock_nq(ghs); - } - - e = kcalloc(num_gh, sizeof(struct gfs2_holder *), GFP_KERNEL); - if (!e) - return -ENOMEM; - - for (x = 0; x < num_gh; x++) { - ghs[x].gh_flags |= LM_FLAG_TRY | GL_ASYNC; - error = gfs2_glock_nq(&ghs[x]); - if (error) { - borked = 1; - serious = error; - num_gh = x; + default: + if (num_gh <= 4) break; - } - } - - for (x = 0; x < num_gh; x++) { - error = e[x] = glock_wait_internal(&ghs[x]); - if (error) { - borked = 1; - if (error != GLR_TRYFAILED && error != GLR_CANCELED) - serious = error; - } + pph = kmalloc(num_gh * sizeof(struct gfs2_holder *), GFP_NOFS); + if (!pph) + return -ENOMEM; } - if (!borked) { - kfree(e); - return 0; - } - - for (x = 0; x < num_gh; x++) - if (!e[x]) - gfs2_glock_dq(&ghs[x]); - - if (serious) - error = serious; - else { - for (x = 0; x < num_gh; x++) - gfs2_holder_reinit(ghs[x].gh_state, ghs[x].gh_flags, - &ghs[x]); - error = nq_m_sync(num_gh, ghs, (struct gfs2_holder **)e); - } + error = nq_m_sync(num_gh, ghs, pph); - kfree(e); + if (pph != tmp) + kfree(pph); return error; } @@ -1456,7 +1448,7 @@ static void blocking_cb(struct gfs2_sbd *sdp, struct lm_lockname *name, if (!gl) return; - handle_callback(gl, state); + handle_callback(gl, state, 1); spin_lock(&gl->gl_spin); run_queue(gl); @@ -1596,7 +1588,7 @@ void gfs2_reclaim_glock(struct gfs2_sbd *sdp) if (gfs2_glmutex_trylock(gl)) { if (list_empty(&gl->gl_holders) && gl->gl_state != LM_ST_UNLOCKED && demote_ok(gl)) - handle_callback(gl, LM_ST_UNLOCKED); + handle_callback(gl, LM_ST_UNLOCKED, 0); gfs2_glmutex_unlock(gl); } @@ -1709,7 +1701,7 @@ static void clear_glock(struct gfs2_glock *gl) if (gfs2_glmutex_trylock(gl)) { if (list_empty(&gl->gl_holders) && gl->gl_state != LM_ST_UNLOCKED) - handle_callback(gl, LM_ST_UNLOCKED); + handle_callback(gl, LM_ST_UNLOCKED, 0); gfs2_glmutex_unlock(gl); } } @@ -1823,7 +1815,8 @@ static int dump_inode(struct glock_iter *gi, struct gfs2_inode *ip) print_dbg(gi, " Inode:\n"); print_dbg(gi, " num = %llu/%llu\n", - ip->i_num.no_formal_ino, ip->i_num.no_addr); + (unsigned long long)ip->i_no_formal_ino, + (unsigned long long)ip->i_no_addr); print_dbg(gi, " type = %u\n", IF2DT(ip->i_inode.i_mode)); print_dbg(gi, " i_flags ="); for (x = 0; x < 32; x++) @@ -1909,8 +1902,8 @@ static int dump_glock(struct glock_iter *gi, struct gfs2_glock *gl) } if (test_bit(GLF_DEMOTE, &gl->gl_flags)) { print_dbg(gi, " Demotion req to state %u (%llu uS ago)\n", - gl->gl_demote_state, - (u64)(jiffies - gl->gl_demote_time)*(1000000/HZ)); + gl->gl_demote_state, (unsigned long long) + (jiffies - gl->gl_demote_time)*(1000000/HZ)); } if (gl->gl_ops == &gfs2_inode_glops && gl->gl_object) { if (!test_bit(GLF_LOCK, &gl->gl_flags) && diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index b3e152db70c..7721ca3fff9 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -87,6 +87,7 @@ int gfs2_glock_nq(struct gfs2_holder *gh); int gfs2_glock_poll(struct gfs2_holder *gh); int gfs2_glock_wait(struct gfs2_holder *gh); void gfs2_glock_dq(struct gfs2_holder *gh); +void gfs2_glock_dq_wait(struct gfs2_holder *gh); void gfs2_glock_dq_uninit(struct gfs2_holder *gh); int gfs2_glock_nq_num(struct gfs2_sbd *sdp, diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 7b82657a991..777ca46010e 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -156,9 +156,9 @@ static void inode_go_sync(struct gfs2_glock *gl) ip = NULL; if (test_bit(GLF_DIRTY, &gl->gl_flags)) { - gfs2_log_flush(gl->gl_sbd, gl); if (ip) filemap_fdatawrite(ip->i_inode.i_mapping); + gfs2_log_flush(gl->gl_sbd, gl); gfs2_meta_sync(gl); if (ip) { struct address_space *mapping = ip->i_inode.i_mapping; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index d995441373a..170ba93829c 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -28,6 +28,14 @@ struct gfs2_sbd; typedef void (*gfs2_glop_bh_t) (struct gfs2_glock *gl, unsigned int ret); +struct gfs2_log_header_host { + u64 lh_sequence; /* Sequence number of this transaction */ + u32 lh_flags; /* GFS2_LOG_HEAD_... */ + u32 lh_tail; /* Block number of log tail */ + u32 lh_blkno; + u32 lh_hash; +}; + /* * Structure of operations that are associated with each * type of element in the log. @@ -60,12 +68,23 @@ struct gfs2_bitmap { u32 bi_len; }; +struct gfs2_rgrp_host { + u32 rg_flags; + u32 rg_free; + u32 rg_dinodes; + u64 rg_igeneration; +}; + struct gfs2_rgrpd { struct list_head rd_list; /* Link with superblock */ struct list_head rd_list_mru; struct list_head rd_recent; /* Recently used rgrps */ struct gfs2_glock *rd_gl; /* Glock for this rgrp */ - struct gfs2_rindex_host rd_ri; + u64 rd_addr; /* grp block disk address */ + u64 rd_data0; /* first data location */ + u32 rd_length; /* length of rgrp header in fs blocks */ + u32 rd_data; /* num of data blocks in rgrp */ + u32 rd_bitbytes; /* number of bytes in data bitmaps */ struct gfs2_rgrp_host rd_rg; u64 rd_rg_vn; struct gfs2_bitmap *rd_bits; @@ -76,6 +95,8 @@ struct gfs2_rgrpd { u32 rd_last_alloc_data; u32 rd_last_alloc_meta; struct gfs2_sbd *rd_sbd; + unsigned long rd_flags; +#define GFS2_RDF_CHECK 0x0001 /* Need to check for unlinked inodes */ }; enum gfs2_state_bits { @@ -211,10 +232,24 @@ enum { GIF_SW_PAGED = 3, }; +struct gfs2_dinode_host { + u64 di_size; /* number of bytes in file */ + u64 di_blocks; /* number of blocks in file */ + u64 di_goal_meta; /* rgrp to alloc from next */ + u64 di_goal_data; /* data block goal */ + u64 di_generation; /* generation number for NFS */ + u32 di_flags; /* GFS2_DIF_... */ + u16 di_height; /* height of metadata */ + /* These only apply to directories */ + u16 di_depth; /* Number of bits in the table */ + u32 di_entries; /* The number of entries in the directory */ + u64 di_eattr; /* extended attribute block number */ +}; + struct gfs2_inode { struct inode i_inode; - struct gfs2_inum_host i_num; - + u64 i_no_addr; + u64 i_no_formal_ino; unsigned long i_flags; /* GIF_... */ struct gfs2_dinode_host i_di; /* To be replaced by ref to block */ @@ -275,14 +310,6 @@ enum { QDF_LOCKED = 2, }; -struct gfs2_quota_lvb { - __be32 qb_magic; - u32 __pad; - __be64 qb_limit; /* Hard limit of # blocks to alloc */ - __be64 qb_warn; /* Warn user when alloc is above this # */ - __be64 qb_value; /* Current # blocks allocated */ -}; - struct gfs2_quota_data { struct list_head qd_list; unsigned int qd_count; @@ -327,7 +354,9 @@ struct gfs2_trans { unsigned int tr_num_buf; unsigned int tr_num_buf_new; + unsigned int tr_num_databuf_new; unsigned int tr_num_buf_rm; + unsigned int tr_num_databuf_rm; struct list_head tr_list_buf; unsigned int tr_num_revoke; @@ -354,6 +383,12 @@ struct gfs2_jdesc { unsigned int jd_blocks; }; +struct gfs2_statfs_change_host { + s64 sc_total; + s64 sc_free; + s64 sc_dinodes; +}; + #define GFS2_GLOCKD_DEFAULT 1 #define GFS2_GLOCKD_MAX 16 @@ -426,6 +461,28 @@ enum { #define GFS2_FSNAME_LEN 256 +struct gfs2_inum_host { + u64 no_formal_ino; + u64 no_addr; +}; + +struct gfs2_sb_host { + u32 sb_magic; + u32 sb_type; + u32 sb_format; + + u32 sb_fs_format; + u32 sb_multihost_format; + u32 sb_bsize; + u32 sb_bsize_shift; + + struct gfs2_inum_host sb_master_dir; + struct gfs2_inum_host sb_root_dir; + + char sb_lockproto[GFS2_LOCKNAME_LEN]; + char sb_locktable[GFS2_LOCKNAME_LEN]; +}; + struct gfs2_sbd { struct super_block *sd_vfs; struct super_block *sd_vfs_meta; @@ -544,6 +601,7 @@ struct gfs2_sbd { unsigned int sd_log_blks_reserved; unsigned int sd_log_commited_buf; + unsigned int sd_log_commited_databuf; unsigned int sd_log_commited_revoke; unsigned int sd_log_num_gl; @@ -552,7 +610,6 @@ struct gfs2_sbd { unsigned int sd_log_num_rg; unsigned int sd_log_num_databuf; unsigned int sd_log_num_jdata; - unsigned int sd_log_num_hdrs; struct list_head sd_log_le_gl; struct list_head sd_log_le_buf; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index df0b8b3018b..34f7bcdea1e 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -38,12 +38,17 @@ #include "trans.h" #include "util.h" +struct gfs2_inum_range_host { + u64 ir_start; + u64 ir_length; +}; + static int iget_test(struct inode *inode, void *opaque) { struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_inum_host *inum = opaque; + u64 *no_addr = opaque; - if (ip->i_num.no_addr == inum->no_addr && + if (ip->i_no_addr == *no_addr && inode->i_private != NULL) return 1; @@ -53,37 +58,70 @@ static int iget_test(struct inode *inode, void *opaque) static int iget_set(struct inode *inode, void *opaque) { struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_inum_host *inum = opaque; + u64 *no_addr = opaque; - ip->i_num = *inum; - inode->i_ino = inum->no_addr; + inode->i_ino = (unsigned long)*no_addr; + ip->i_no_addr = *no_addr; return 0; } -struct inode *gfs2_ilookup(struct super_block *sb, struct gfs2_inum_host *inum) +struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr) +{ + unsigned long hash = (unsigned long)no_addr; + return ilookup5(sb, hash, iget_test, &no_addr); +} + +static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr) { - return ilookup5(sb, (unsigned long)inum->no_addr, - iget_test, inum); + unsigned long hash = (unsigned long)no_addr; + return iget5_locked(sb, hash, iget_test, iget_set, &no_addr); } -static struct inode *gfs2_iget(struct super_block *sb, struct gfs2_inum_host *inum) +/** + * GFS2 lookup code fills in vfs inode contents based on info obtained + * from directory entry inside gfs2_inode_lookup(). This has caused issues + * with NFS code path since its get_dentry routine doesn't have the relevant + * directory entry when gfs2_inode_lookup() is invoked. Part of the code + * segment inside gfs2_inode_lookup code needs to get moved around. + * + * Clean up I_LOCK and I_NEW as well. + **/ + +void gfs2_set_iop(struct inode *inode) { - return iget5_locked(sb, (unsigned long)inum->no_addr, - iget_test, iget_set, inum); + umode_t mode = inode->i_mode; + + if (S_ISREG(mode)) { + inode->i_op = &gfs2_file_iops; + inode->i_fop = &gfs2_file_fops; + inode->i_mapping->a_ops = &gfs2_file_aops; + } else if (S_ISDIR(mode)) { + inode->i_op = &gfs2_dir_iops; + inode->i_fop = &gfs2_dir_fops; + } else if (S_ISLNK(mode)) { + inode->i_op = &gfs2_symlink_iops; + } else { + inode->i_op = &gfs2_dev_iops; + } + + unlock_new_inode(inode); } /** * gfs2_inode_lookup - Lookup an inode * @sb: The super block - * @inum: The inode number + * @no_addr: The inode number * @type: The type of the inode * * Returns: A VFS inode, or an error */ -struct inode *gfs2_inode_lookup(struct super_block *sb, struct gfs2_inum_host *inum, unsigned int type) +struct inode *gfs2_inode_lookup(struct super_block *sb, + unsigned int type, + u64 no_addr, + u64 no_formal_ino) { - struct inode *inode = gfs2_iget(sb, inum); + struct inode *inode = gfs2_iget(sb, no_addr); struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_glock *io_gl; int error; @@ -93,29 +131,15 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, struct gfs2_inum_host *i if (inode->i_state & I_NEW) { struct gfs2_sbd *sdp = GFS2_SB(inode); - umode_t mode = DT2IF(type); inode->i_private = ip; - inode->i_mode = mode; - - if (S_ISREG(mode)) { - inode->i_op = &gfs2_file_iops; - inode->i_fop = &gfs2_file_fops; - inode->i_mapping->a_ops = &gfs2_file_aops; - } else if (S_ISDIR(mode)) { - inode->i_op = &gfs2_dir_iops; - inode->i_fop = &gfs2_dir_fops; - } else if (S_ISLNK(mode)) { - inode->i_op = &gfs2_symlink_iops; - } else { - inode->i_op = &gfs2_dev_iops; - } + ip->i_no_formal_ino = no_formal_ino; - error = gfs2_glock_get(sdp, inum->no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); + error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); if (unlikely(error)) goto fail; ip->i_gl->gl_object = ip; - error = gfs2_glock_get(sdp, inum->no_addr, &gfs2_iopen_glops, CREATE, &io_gl); + error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl); if (unlikely(error)) goto fail_put; @@ -123,12 +147,38 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, struct gfs2_inum_host *i error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh); if (unlikely(error)) goto fail_iopen; + ip->i_iopen_gh.gh_gl->gl_object = ip; gfs2_glock_put(io_gl); - unlock_new_inode(inode); + + if ((type == DT_UNKNOWN) && (no_formal_ino == 0)) + goto gfs2_nfsbypass; + + inode->i_mode = DT2IF(type); + + /* + * We must read the inode in order to work out its type in + * this case. Note that this doesn't happen often as we normally + * know the type beforehand. This code path only occurs during + * unlinked inode recovery (where it is safe to do this glock, + * which is not true in the general case). + */ + if (type == DT_UNKNOWN) { + struct gfs2_holder gh; + error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); + if (unlikely(error)) + goto fail_glock; + /* Inode is now uptodate */ + gfs2_glock_dq_uninit(&gh); + } + + gfs2_set_iop(inode); } +gfs2_nfsbypass: return inode; +fail_glock: + gfs2_glock_dq(&ip->i_iopen_gh); fail_iopen: gfs2_glock_put(io_gl); fail_put: @@ -144,14 +194,12 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) struct gfs2_dinode_host *di = &ip->i_di; const struct gfs2_dinode *str = buf; - if (ip->i_num.no_addr != be64_to_cpu(str->di_num.no_addr)) { + if (ip->i_no_addr != be64_to_cpu(str->di_num.no_addr)) { if (gfs2_consist_inode(ip)) gfs2_dinode_print(ip); return -EIO; } - if (ip->i_num.no_formal_ino != be64_to_cpu(str->di_num.no_formal_ino)) - return -ESTALE; - + ip->i_no_formal_ino = be64_to_cpu(str->di_num.no_formal_ino); ip->i_inode.i_mode = be32_to_cpu(str->di_mode); ip->i_inode.i_rdev = 0; switch (ip->i_inode.i_mode & S_IFMT) { @@ -175,11 +223,11 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) di->di_blocks = be64_to_cpu(str->di_blocks); gfs2_set_inode_blocks(&ip->i_inode); ip->i_inode.i_atime.tv_sec = be64_to_cpu(str->di_atime); - ip->i_inode.i_atime.tv_nsec = 0; + ip->i_inode.i_atime.tv_nsec = be32_to_cpu(str->di_atime_nsec); ip->i_inode.i_mtime.tv_sec = be64_to_cpu(str->di_mtime); - ip->i_inode.i_mtime.tv_nsec = 0; + ip->i_inode.i_mtime.tv_nsec = be32_to_cpu(str->di_mtime_nsec); ip->i_inode.i_ctime.tv_sec = be64_to_cpu(str->di_ctime); - ip->i_inode.i_ctime.tv_nsec = 0; + ip->i_inode.i_ctime.tv_nsec = be32_to_cpu(str->di_ctime_nsec); di->di_goal_meta = be64_to_cpu(str->di_goal_meta); di->di_goal_data = be64_to_cpu(str->di_goal_data); @@ -247,7 +295,7 @@ int gfs2_dinode_dealloc(struct gfs2_inode *ip) if (error) goto out_qs; - rgd = gfs2_blk2rgrpd(sdp, ip->i_num.no_addr); + rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr); if (!rgd) { gfs2_consist_inode(ip); error = -EIO; @@ -314,7 +362,7 @@ int gfs2_change_nlink(struct gfs2_inode *ip, int diff) else drop_nlink(&ip->i_inode); - ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); @@ -366,9 +414,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, struct super_block *sb = dir->i_sb; struct gfs2_inode *dip = GFS2_I(dir); struct gfs2_holder d_gh; - struct gfs2_inum_host inum; - unsigned int type; - int error; + int error = 0; struct inode *inode = NULL; int unlock = 0; @@ -395,12 +441,9 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, goto out; } - error = gfs2_dir_search(dir, name, &inum, &type); - if (error) - goto out; - - inode = gfs2_inode_lookup(sb, &inum, type); - + inode = gfs2_dir_search(dir, name); + if (IS_ERR(inode)) + error = PTR_ERR(inode); out: if (unlock) gfs2_glock_dq_uninit(&d_gh); @@ -409,6 +452,22 @@ out: return inode ? inode : ERR_PTR(error); } +static void gfs2_inum_range_in(struct gfs2_inum_range_host *ir, const void *buf) +{ + const struct gfs2_inum_range *str = buf; + + ir->ir_start = be64_to_cpu(str->ir_start); + ir->ir_length = be64_to_cpu(str->ir_length); +} + +static void gfs2_inum_range_out(const struct gfs2_inum_range_host *ir, void *buf) +{ + struct gfs2_inum_range *str = buf; + + str->ir_start = cpu_to_be64(ir->ir_start); + str->ir_length = cpu_to_be64(ir->ir_length); +} + static int pick_formal_ino_1(struct gfs2_sbd *sdp, u64 *formal_ino) { struct gfs2_inode *ip = GFS2_I(sdp->sd_ir_inode); @@ -548,7 +607,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name, if (!dip->i_inode.i_nlink) return -EPERM; - error = gfs2_dir_search(&dip->i_inode, name, NULL, NULL); + error = gfs2_dir_check(&dip->i_inode, name, NULL); switch (error) { case -ENOENT: error = 0; @@ -588,8 +647,7 @@ static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode, *gid = current->fsgid; } -static int alloc_dinode(struct gfs2_inode *dip, struct gfs2_inum_host *inum, - u64 *generation) +static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation) { struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); int error; @@ -605,7 +663,7 @@ static int alloc_dinode(struct gfs2_inode *dip, struct gfs2_inum_host *inum, if (error) goto out_ipreserv; - inum->no_addr = gfs2_alloc_di(dip, generation); + *no_addr = gfs2_alloc_di(dip, generation); gfs2_trans_end(sdp); @@ -635,6 +693,7 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_dinode *di; struct buffer_head *dibh; + struct timespec tv = CURRENT_TIME; dibh = gfs2_meta_new(gl, inum->no_addr); gfs2_trans_add_bh(gl, dibh, 1); @@ -650,7 +709,7 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, di->di_nlink = 0; di->di_size = 0; di->di_blocks = cpu_to_be64(1); - di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(get_seconds()); + di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(tv.tv_sec); di->di_major = cpu_to_be32(MAJOR(dev)); di->di_minor = cpu_to_be32(MINOR(dev)); di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr); @@ -680,6 +739,9 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, di->di_entries = 0; memset(&di->__pad4, 0, sizeof(di->__pad4)); di->di_eattr = 0; + di->di_atime_nsec = cpu_to_be32(tv.tv_nsec); + di->di_mtime_nsec = cpu_to_be32(tv.tv_nsec); + di->di_ctime_nsec = cpu_to_be32(tv.tv_nsec); memset(&di->di_reserved, 0, sizeof(di->di_reserved)); brelse(dibh); @@ -749,7 +811,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, goto fail_quota_locks; error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_ri.ri_length + + al->al_rgd->rd_length + 2 * RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) @@ -760,7 +822,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, goto fail_quota_locks; } - error = gfs2_dir_add(&dip->i_inode, name, &ip->i_num, IF2DT(ip->i_inode.i_mode)); + error = gfs2_dir_add(&dip->i_inode, name, ip, IF2DT(ip->i_inode.i_mode)); if (error) goto fail_end_trans; @@ -840,11 +902,11 @@ static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip) struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, unsigned int mode, dev_t dev) { - struct inode *inode; + struct inode *inode = NULL; struct gfs2_inode *dip = ghs->gh_gl->gl_object; struct inode *dir = &dip->i_inode; struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_inum_host inum; + struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; int error; u64 generation; @@ -864,7 +926,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, if (error) goto fail_gunlock; - error = alloc_dinode(dip, &inum, &generation); + error = alloc_dinode(dip, &inum.no_addr, &generation); if (error) goto fail_gunlock; @@ -877,34 +939,36 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, if (error) goto fail_gunlock2; - inode = gfs2_inode_lookup(dir->i_sb, &inum, IF2DT(mode)); + inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), + inum.no_addr, + inum.no_formal_ino); if (IS_ERR(inode)) goto fail_gunlock2; error = gfs2_inode_refresh(GFS2_I(inode)); if (error) - goto fail_iput; + goto fail_gunlock2; error = gfs2_acl_create(dip, GFS2_I(inode)); if (error) - goto fail_iput; + goto fail_gunlock2; error = gfs2_security_init(dip, GFS2_I(inode)); if (error) - goto fail_iput; + goto fail_gunlock2; error = link_dinode(dip, name, GFS2_I(inode)); if (error) - goto fail_iput; + goto fail_gunlock2; if (!inode) return ERR_PTR(-ENOMEM); return inode; -fail_iput: - iput(inode); fail_gunlock2: gfs2_glock_dq_uninit(ghs + 1); + if (inode) + iput(inode); fail_gunlock: gfs2_glock_dq(ghs); fail: @@ -976,10 +1040,8 @@ int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, */ int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip) + const struct gfs2_inode *ip) { - struct gfs2_inum_host inum; - unsigned int type; int error; if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode)) @@ -997,18 +1059,10 @@ int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, if (error) return error; - error = gfs2_dir_search(&dip->i_inode, name, &inum, &type); + error = gfs2_dir_check(&dip->i_inode, name, ip); if (error) return error; - if (!gfs2_inum_equal(&inum, &ip->i_num)) - return -ENOENT; - - if (IF2DT(ip->i_inode.i_mode) != type) { - gfs2_consist_inode(dip); - return -EIO; - } - return 0; } @@ -1132,10 +1186,11 @@ int gfs2_glock_nq_atime(struct gfs2_holder *gh) struct gfs2_glock *gl = gh->gh_gl; struct gfs2_sbd *sdp = gl->gl_sbd; struct gfs2_inode *ip = gl->gl_object; - s64 curtime, quantum = gfs2_tune_get(sdp, gt_atime_quantum); + s64 quantum = gfs2_tune_get(sdp, gt_atime_quantum); unsigned int state; int flags; int error; + struct timespec tv = CURRENT_TIME; if (gfs2_assert_warn(sdp, gh->gh_flags & GL_ATIME) || gfs2_assert_warn(sdp, !(gh->gh_flags & GL_ASYNC)) || @@ -1153,8 +1208,7 @@ int gfs2_glock_nq_atime(struct gfs2_holder *gh) (sdp->sd_vfs->s_flags & MS_RDONLY)) return 0; - curtime = get_seconds(); - if (curtime - ip->i_inode.i_atime.tv_sec >= quantum) { + if (tv.tv_sec - ip->i_inode.i_atime.tv_sec >= quantum) { gfs2_glock_dq(gh); gfs2_holder_reinit(LM_ST_EXCLUSIVE, gh->gh_flags & ~LM_FLAG_ANY, gh); @@ -1165,8 +1219,8 @@ int gfs2_glock_nq_atime(struct gfs2_holder *gh) /* Verify that atime hasn't been updated while we were trying to get exclusive lock. */ - curtime = get_seconds(); - if (curtime - ip->i_inode.i_atime.tv_sec >= quantum) { + tv = CURRENT_TIME; + if (tv.tv_sec - ip->i_inode.i_atime.tv_sec >= quantum) { struct buffer_head *dibh; struct gfs2_dinode *di; @@ -1180,11 +1234,12 @@ int gfs2_glock_nq_atime(struct gfs2_holder *gh) if (error) goto fail_end_trans; - ip->i_inode.i_atime.tv_sec = curtime; + ip->i_inode.i_atime = tv; gfs2_trans_add_bh(ip->i_gl, dibh, 1); di = (struct gfs2_dinode *)dibh->b_data; di->di_atime = cpu_to_be64(ip->i_inode.i_atime.tv_sec); + di->di_atime_nsec = cpu_to_be32(ip->i_inode.i_atime.tv_nsec); brelse(dibh); gfs2_trans_end(sdp); @@ -1252,3 +1307,66 @@ int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) return error; } +void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf) +{ + const struct gfs2_dinode_host *di = &ip->i_di; + struct gfs2_dinode *str = buf; + + str->di_header.mh_magic = cpu_to_be32(GFS2_MAGIC); + str->di_header.mh_type = cpu_to_be32(GFS2_METATYPE_DI); + str->di_header.__pad0 = 0; + str->di_header.mh_format = cpu_to_be32(GFS2_FORMAT_DI); + str->di_header.__pad1 = 0; + str->di_num.no_addr = cpu_to_be64(ip->i_no_addr); + str->di_num.no_formal_ino = cpu_to_be64(ip->i_no_formal_ino); + str->di_mode = cpu_to_be32(ip->i_inode.i_mode); + str->di_uid = cpu_to_be32(ip->i_inode.i_uid); + str->di_gid = cpu_to_be32(ip->i_inode.i_gid); + str->di_nlink = cpu_to_be32(ip->i_inode.i_nlink); + str->di_size = cpu_to_be64(di->di_size); + str->di_blocks = cpu_to_be64(di->di_blocks); + str->di_atime = cpu_to_be64(ip->i_inode.i_atime.tv_sec); + str->di_mtime = cpu_to_be64(ip->i_inode.i_mtime.tv_sec); + str->di_ctime = cpu_to_be64(ip->i_inode.i_ctime.tv_sec); + + str->di_goal_meta = cpu_to_be64(di->di_goal_meta); + str->di_goal_data = cpu_to_be64(di->di_goal_data); + str->di_generation = cpu_to_be64(di->di_generation); + + str->di_flags = cpu_to_be32(di->di_flags); + str->di_height = cpu_to_be16(di->di_height); + str->di_payload_format = cpu_to_be32(S_ISDIR(ip->i_inode.i_mode) && + !(ip->i_di.di_flags & GFS2_DIF_EXHASH) ? + GFS2_FORMAT_DE : 0); + str->di_depth = cpu_to_be16(di->di_depth); + str->di_entries = cpu_to_be32(di->di_entries); + + str->di_eattr = cpu_to_be64(di->di_eattr); + str->di_atime_nsec = cpu_to_be32(ip->i_inode.i_atime.tv_nsec); + str->di_mtime_nsec = cpu_to_be32(ip->i_inode.i_mtime.tv_nsec); + str->di_ctime_nsec = cpu_to_be32(ip->i_inode.i_ctime.tv_nsec); +} + +void gfs2_dinode_print(const struct gfs2_inode *ip) +{ + const struct gfs2_dinode_host *di = &ip->i_di; + + printk(KERN_INFO " no_formal_ino = %llu\n", + (unsigned long long)ip->i_no_formal_ino); + printk(KERN_INFO " no_addr = %llu\n", + (unsigned long long)ip->i_no_addr); + printk(KERN_INFO " di_size = %llu\n", (unsigned long long)di->di_size); + printk(KERN_INFO " di_blocks = %llu\n", + (unsigned long long)di->di_blocks); + printk(KERN_INFO " di_goal_meta = %llu\n", + (unsigned long long)di->di_goal_meta); + printk(KERN_INFO " di_goal_data = %llu\n", + (unsigned long long)di->di_goal_data); + printk(KERN_INFO " di_flags = 0x%.8X\n", di->di_flags); + printk(KERN_INFO " di_height = %u\n", di->di_height); + printk(KERN_INFO " di_depth = %u\n", di->di_depth); + printk(KERN_INFO " di_entries = %u\n", di->di_entries); + printk(KERN_INFO " di_eattr = %llu\n", + (unsigned long long)di->di_eattr); +} + diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index b57f448b15b..4517ac82c01 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -10,17 +10,17 @@ #ifndef __INODE_DOT_H__ #define __INODE_DOT_H__ -static inline int gfs2_is_stuffed(struct gfs2_inode *ip) +static inline int gfs2_is_stuffed(const struct gfs2_inode *ip) { return !ip->i_di.di_height; } -static inline int gfs2_is_jdata(struct gfs2_inode *ip) +static inline int gfs2_is_jdata(const struct gfs2_inode *ip) { return ip->i_di.di_flags & GFS2_DIF_JDATA; } -static inline int gfs2_is_dir(struct gfs2_inode *ip) +static inline int gfs2_is_dir(const struct gfs2_inode *ip) { return S_ISDIR(ip->i_inode.i_mode); } @@ -32,9 +32,25 @@ static inline void gfs2_set_inode_blocks(struct inode *inode) (GFS2_SB(inode)->sd_sb.sb_bsize_shift - GFS2_BASIC_BLOCK_SHIFT); } +static inline int gfs2_check_inum(const struct gfs2_inode *ip, u64 no_addr, + u64 no_formal_ino) +{ + return ip->i_no_addr == no_addr && ip->i_no_formal_ino == no_formal_ino; +} + +static inline void gfs2_inum_out(const struct gfs2_inode *ip, + struct gfs2_dirent *dent) +{ + dent->de_inum.no_formal_ino = cpu_to_be64(ip->i_no_formal_ino); + dent->de_inum.no_addr = cpu_to_be64(ip->i_no_addr); +} + + void gfs2_inode_attr_in(struct gfs2_inode *ip); -struct inode *gfs2_inode_lookup(struct super_block *sb, struct gfs2_inum_host *inum, unsigned type); -struct inode *gfs2_ilookup(struct super_block *sb, struct gfs2_inum_host *inum); +void gfs2_set_iop(struct inode *inode); +struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, + u64 no_addr, u64 no_formal_ino); +struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr); int gfs2_inode_refresh(struct gfs2_inode *ip); @@ -47,12 +63,14 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, struct gfs2_inode *ip); int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip); + const struct gfs2_inode *ip); int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to); int gfs2_readlinki(struct gfs2_inode *ip, char **buf, unsigned int *len); int gfs2_glock_nq_atime(struct gfs2_holder *gh); int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr); struct inode *gfs2_lookup_simple(struct inode *dip, const char *name); +void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf); +void gfs2_dinode_print(const struct gfs2_inode *ip); #endif /* __INODE_DOT_H__ */ diff --git a/fs/gfs2/locking/dlm/lock.c b/fs/gfs2/locking/dlm/lock.c index c305255bfe8..542a797ac89 100644 --- a/fs/gfs2/locking/dlm/lock.c +++ b/fs/gfs2/locking/dlm/lock.c @@ -174,7 +174,6 @@ static int gdlm_create_lp(struct gdlm_ls *ls, struct lm_lockname *name, lp->cur = DLM_LOCK_IV; lp->lvb = NULL; lp->hold_null = NULL; - init_completion(&lp->ast_wait); INIT_LIST_HEAD(&lp->clist); INIT_LIST_HEAD(&lp->blist); INIT_LIST_HEAD(&lp->delay_list); @@ -399,6 +398,12 @@ static void gdlm_del_lvb(struct gdlm_lock *lp) lp->lksb.sb_lvbptr = NULL; } +static int gdlm_ast_wait(void *word) +{ + schedule(); + return 0; +} + /* This can do a synchronous dlm request (requiring a lock_dlm thread to get the completion) because gfs won't call hold_lvb() during a callback (from the context of a lock_dlm thread). */ @@ -424,10 +429,10 @@ static int hold_null_lock(struct gdlm_lock *lp) lpn->lkf = DLM_LKF_VALBLK | DLM_LKF_EXPEDITE; set_bit(LFL_NOBAST, &lpn->flags); set_bit(LFL_INLOCK, &lpn->flags); + set_bit(LFL_AST_WAIT, &lpn->flags); - init_completion(&lpn->ast_wait); gdlm_do_lock(lpn); - wait_for_completion(&lpn->ast_wait); + wait_on_bit(&lpn->flags, LFL_AST_WAIT, gdlm_ast_wait, TASK_UNINTERRUPTIBLE); error = lpn->lksb.sb_status; if (error) { printk(KERN_INFO "lock_dlm: hold_null_lock dlm error %d\n", diff --git a/fs/gfs2/locking/dlm/lock_dlm.h b/fs/gfs2/locking/dlm/lock_dlm.h index d074c6e6f9b..24d70f73b65 100644 --- a/fs/gfs2/locking/dlm/lock_dlm.h +++ b/fs/gfs2/locking/dlm/lock_dlm.h @@ -101,6 +101,7 @@ enum { LFL_NOBAST = 10, LFL_HEADQUE = 11, LFL_UNLOCK_DELETE = 12, + LFL_AST_WAIT = 13, }; struct gdlm_lock { @@ -117,7 +118,6 @@ struct gdlm_lock { unsigned long flags; /* lock_dlm flags LFL_ */ int bast_mode; /* protected by async_lock */ - struct completion ast_wait; struct list_head clist; /* complete */ struct list_head blist; /* blocking */ diff --git a/fs/gfs2/locking/dlm/mount.c b/fs/gfs2/locking/dlm/mount.c index 1d8faa3da8a..41c5b04caab 100644 --- a/fs/gfs2/locking/dlm/mount.c +++ b/fs/gfs2/locking/dlm/mount.c @@ -147,7 +147,7 @@ static int gdlm_mount(char *table_name, char *host_data, error = dlm_new_lockspace(ls->fsname, strlen(ls->fsname), &ls->dlm_lockspace, - nodir ? DLM_LSFL_NODIR : 0, + DLM_LSFL_FS | (nodir ? DLM_LSFL_NODIR : 0), GDLM_LVB_SIZE); if (error) { log_error("dlm_new_lockspace error %d", error); diff --git a/fs/gfs2/locking/dlm/plock.c b/fs/gfs2/locking/dlm/plock.c index f82495e18c2..fba1f1d87e4 100644 --- a/fs/gfs2/locking/dlm/plock.c +++ b/fs/gfs2/locking/dlm/plock.c @@ -242,7 +242,7 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name, op->info.number = name->ln_number; op->info.start = fl->fl_start; op->info.end = fl->fl_end; - + op->info.owner = (__u64)(long) fl->fl_owner; send_op(op); wait_event(recv_wq, (op->done != 0)); @@ -254,16 +254,20 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name, } spin_unlock(&ops_lock); + /* info.rv from userspace is 1 for conflict, 0 for no-conflict, + -ENOENT if there are no locks on the file */ + rv = op->info.rv; fl->fl_type = F_UNLCK; if (rv == -ENOENT) rv = 0; - else if (rv == 0 && op->info.pid != fl->fl_pid) { + else if (rv > 0) { fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK; fl->fl_pid = op->info.pid; fl->fl_start = op->info.start; fl->fl_end = op->info.end; + rv = 0; } kfree(op); diff --git a/fs/gfs2/locking/dlm/thread.c b/fs/gfs2/locking/dlm/thread.c index 9cf1f168eaf..1aca51e4509 100644 --- a/fs/gfs2/locking/dlm/thread.c +++ b/fs/gfs2/locking/dlm/thread.c @@ -44,6 +44,13 @@ static void process_blocking(struct gdlm_lock *lp, int bast_mode) ls->fscb(ls->sdp, cb, &lp->lockname); } +static void wake_up_ast(struct gdlm_lock *lp) +{ + clear_bit(LFL_AST_WAIT, &lp->flags); + smp_mb__after_clear_bit(); + wake_up_bit(&lp->flags, LFL_AST_WAIT); +} + static void process_complete(struct gdlm_lock *lp) { struct gdlm_ls *ls = lp->ls; @@ -136,7 +143,7 @@ static void process_complete(struct gdlm_lock *lp) */ if (test_and_clear_bit(LFL_SYNC_LVB, &lp->flags)) { - complete(&lp->ast_wait); + wake_up_ast(lp); return; } @@ -214,7 +221,7 @@ out: if (test_bit(LFL_INLOCK, &lp->flags)) { clear_bit(LFL_NOBLOCK, &lp->flags); lp->cur = lp->req; - complete(&lp->ast_wait); + wake_up_ast(lp); return; } diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 291415ddfe5..f49a12e2408 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -83,6 +83,11 @@ static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) gfs2_assert(sdp, bd->bd_ail == ai); + if (!bh){ + list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); + continue; + } + if (!buffer_busy(bh)) { if (!buffer_uptodate(bh)) { gfs2_log_unlock(sdp); @@ -125,6 +130,11 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl bd_ail_st_list) { bh = bd->bd_bh; + if (!bh){ + list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); + continue; + } + gfs2_assert(sdp, bd->bd_ail == ai); if (buffer_busy(bh)) { @@ -262,8 +272,8 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail) * @sdp: The GFS2 superblock * @blks: The number of blocks to reserve * - * Note that we never give out the last 6 blocks of the journal. Thats - * due to the fact that there is are a small number of header blocks + * Note that we never give out the last few blocks of the journal. Thats + * due to the fact that there is a small number of header blocks * associated with each log flush. The exact number can't be known until * flush time, so we ensure that we have just enough free blocks at all * times to avoid running out during a log flush. @@ -274,6 +284,7 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail) int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks) { unsigned int try = 0; + unsigned reserved_blks = 6 * (4096 / sdp->sd_vfs->s_blocksize); if (gfs2_assert_warn(sdp, blks) || gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks)) @@ -281,7 +292,7 @@ int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks) mutex_lock(&sdp->sd_log_reserve_mutex); gfs2_log_lock(sdp); - while(sdp->sd_log_blks_free <= (blks + 6)) { + while(sdp->sd_log_blks_free <= (blks + reserved_blks)) { gfs2_log_unlock(sdp); gfs2_ail1_empty(sdp, 0); gfs2_log_flush(sdp, NULL); @@ -357,6 +368,58 @@ static inline unsigned int log_distance(struct gfs2_sbd *sdp, unsigned int newer return dist; } +/** + * calc_reserved - Calculate the number of blocks to reserve when + * refunding a transaction's unused buffers. + * @sdp: The GFS2 superblock + * + * This is complex. We need to reserve room for all our currently used + * metadata buffers (e.g. normal file I/O rewriting file time stamps) and + * all our journaled data buffers for journaled files (e.g. files in the + * meta_fs like rindex, or files for which chattr +j was done.) + * If we don't reserve enough space, gfs2_log_refund and gfs2_log_flush + * will count it as free space (sd_log_blks_free) and corruption will follow. + * + * We can have metadata bufs and jdata bufs in the same journal. So each + * type gets its own log header, for which we need to reserve a block. + * In fact, each type has the potential for needing more than one header + * in cases where we have more buffers than will fit on a journal page. + * Metadata journal entries take up half the space of journaled buffer entries. + * Thus, metadata entries have buf_limit (502) and journaled buffers have + * databuf_limit (251) before they cause a wrap around. + * + * Also, we need to reserve blocks for revoke journal entries and one for an + * overall header for the lot. + * + * Returns: the number of blocks reserved + */ +static unsigned int calc_reserved(struct gfs2_sbd *sdp) +{ + unsigned int reserved = 0; + unsigned int mbuf_limit, metabufhdrs_needed; + unsigned int dbuf_limit, databufhdrs_needed; + unsigned int revokes = 0; + + mbuf_limit = buf_limit(sdp); + metabufhdrs_needed = (sdp->sd_log_commited_buf + + (mbuf_limit - 1)) / mbuf_limit; + dbuf_limit = databuf_limit(sdp); + databufhdrs_needed = (sdp->sd_log_commited_databuf + + (dbuf_limit - 1)) / dbuf_limit; + + if (sdp->sd_log_commited_revoke) + revokes = gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke, + sizeof(u64)); + + reserved = sdp->sd_log_commited_buf + metabufhdrs_needed + + sdp->sd_log_commited_databuf + databufhdrs_needed + + revokes; + /* One for the overall header */ + if (reserved) + reserved++; + return reserved; +} + static unsigned int current_tail(struct gfs2_sbd *sdp) { struct gfs2_ail *ai; @@ -447,14 +510,14 @@ struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp, return bh; } -static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail, int pull) +static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail) { unsigned int dist = log_distance(sdp, new_tail, sdp->sd_log_tail); ail2_empty(sdp, new_tail); gfs2_log_lock(sdp); - sdp->sd_log_blks_free += dist - (pull ? 1 : 0); + sdp->sd_log_blks_free += dist; gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks); gfs2_log_unlock(sdp); @@ -504,7 +567,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) brelse(bh); if (sdp->sd_log_tail != tail) - log_pull_tail(sdp, tail, pull); + log_pull_tail(sdp, tail); else gfs2_assert_withdraw(sdp, !pull); @@ -517,6 +580,7 @@ static void log_flush_commit(struct gfs2_sbd *sdp) struct list_head *head = &sdp->sd_log_flush_list; struct gfs2_log_buf *lb; struct buffer_head *bh; + int flushcount = 0; while (!list_empty(head)) { lb = list_entry(head->next, struct gfs2_log_buf, lb_list); @@ -533,9 +597,20 @@ static void log_flush_commit(struct gfs2_sbd *sdp) } else brelse(bh); kfree(lb); + flushcount++; } - log_write_header(sdp, 0, 0); + /* If nothing was journaled, the header is unplanned and unwanted. */ + if (flushcount) { + log_write_header(sdp, 0, 0); + } else { + unsigned int tail; + tail = current_tail(sdp); + + gfs2_ail1_empty(sdp, 0); + if (sdp->sd_log_tail != tail) + log_pull_tail(sdp, tail); + } } /** @@ -565,7 +640,10 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) INIT_LIST_HEAD(&ai->ai_ail1_list); INIT_LIST_HEAD(&ai->ai_ail2_list); - gfs2_assert_withdraw(sdp, sdp->sd_log_num_buf == sdp->sd_log_commited_buf); + gfs2_assert_withdraw(sdp, + sdp->sd_log_num_buf + sdp->sd_log_num_jdata == + sdp->sd_log_commited_buf + + sdp->sd_log_commited_databuf); gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke); @@ -576,16 +654,19 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) lops_before_commit(sdp); if (!list_empty(&sdp->sd_log_flush_list)) log_flush_commit(sdp); - else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle) + else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){ + gfs2_log_lock(sdp); + sdp->sd_log_blks_free--; /* Adjust for unreserved buffer */ + gfs2_log_unlock(sdp); log_write_header(sdp, 0, PULL); + } lops_after_commit(sdp, ai); gfs2_log_lock(sdp); sdp->sd_log_head = sdp->sd_log_flush_head; - sdp->sd_log_blks_free -= sdp->sd_log_num_hdrs; sdp->sd_log_blks_reserved = 0; sdp->sd_log_commited_buf = 0; - sdp->sd_log_num_hdrs = 0; + sdp->sd_log_commited_databuf = 0; sdp->sd_log_commited_revoke = 0; if (!list_empty(&ai->ai_ail1_list)) { @@ -602,32 +683,26 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { - unsigned int reserved = 0; + unsigned int reserved; unsigned int old; gfs2_log_lock(sdp); sdp->sd_log_commited_buf += tr->tr_num_buf_new - tr->tr_num_buf_rm; - gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_buf) >= 0); + sdp->sd_log_commited_databuf += tr->tr_num_databuf_new - + tr->tr_num_databuf_rm; + gfs2_assert_withdraw(sdp, (((int)sdp->sd_log_commited_buf) >= 0) || + (((int)sdp->sd_log_commited_databuf) >= 0)); sdp->sd_log_commited_revoke += tr->tr_num_revoke - tr->tr_num_revoke_rm; gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_revoke) >= 0); - - if (sdp->sd_log_commited_buf) - reserved += sdp->sd_log_commited_buf; - if (sdp->sd_log_commited_revoke) - reserved += gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke, - sizeof(u64)); - if (reserved) - reserved++; - + reserved = calc_reserved(sdp); old = sdp->sd_log_blks_free; sdp->sd_log_blks_free += tr->tr_reserved - (reserved - sdp->sd_log_blks_reserved); gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free >= old); - gfs2_assert_withdraw(sdp, - sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks + - sdp->sd_log_num_hdrs); + gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <= + sdp->sd_jdesc->jd_blocks); sdp->sd_log_blks_reserved = reserved; @@ -673,13 +748,13 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp) gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_hdrs); gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list)); sdp->sd_log_flush_head = sdp->sd_log_head; sdp->sd_log_flush_wrapped = 0; - log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT, 0); + log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT, + (sdp->sd_log_tail == current_tail(sdp)) ? 0 : PULL); gfs2_assert_warn(sdp, sdp->sd_log_blks_free == sdp->sd_jdesc->jd_blocks); gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail); diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index f82d84d05d2..aff70f0698f 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -17,6 +17,7 @@ #include "gfs2.h" #include "incore.h" +#include "inode.h" #include "glock.h" #include "log.h" #include "lops.h" @@ -117,15 +118,13 @@ static void buf_lo_before_commit(struct gfs2_sbd *sdp) struct gfs2_log_descriptor *ld; struct gfs2_bufdata *bd1 = NULL, *bd2; unsigned int total = sdp->sd_log_num_buf; - unsigned int offset = sizeof(struct gfs2_log_descriptor); + unsigned int offset = BUF_OFFSET; unsigned int limit; unsigned int num; unsigned n; __be64 *ptr; - offset += sizeof(__be64) - 1; - offset &= ~(sizeof(__be64) - 1); - limit = (sdp->sd_sb.sb_bsize - offset)/sizeof(__be64); + limit = buf_limit(sdp); /* for 4k blocks, limit = 503 */ bd1 = bd2 = list_prepare_entry(bd1, &sdp->sd_log_le_buf, bd_le.le_list); @@ -134,7 +133,6 @@ static void buf_lo_before_commit(struct gfs2_sbd *sdp) if (total > limit) num = limit; bh = gfs2_log_get_buf(sdp); - sdp->sd_log_num_hdrs++; ld = (struct gfs2_log_descriptor *)bh->b_data; ptr = (__be64 *)(bh->b_data + offset); ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC); @@ -469,25 +467,28 @@ static void databuf_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) struct gfs2_inode *ip = GFS2_I(mapping->host); gfs2_log_lock(sdp); + if (!list_empty(&bd->bd_list_tr)) { + gfs2_log_unlock(sdp); + return; + } tr->tr_touched = 1; - if (list_empty(&bd->bd_list_tr) && - (ip->i_di.di_flags & GFS2_DIF_JDATA)) { + if (gfs2_is_jdata(ip)) { tr->tr_num_buf++; list_add(&bd->bd_list_tr, &tr->tr_list_buf); - gfs2_log_unlock(sdp); - gfs2_pin(sdp, bd->bd_bh); - tr->tr_num_buf_new++; - } else { - gfs2_log_unlock(sdp); } + gfs2_log_unlock(sdp); + if (!list_empty(&le->le_list)) + return; + gfs2_trans_add_gl(bd->bd_gl); - gfs2_log_lock(sdp); - if (list_empty(&le->le_list)) { - if (ip->i_di.di_flags & GFS2_DIF_JDATA) - sdp->sd_log_num_jdata++; - sdp->sd_log_num_databuf++; - list_add(&le->le_list, &sdp->sd_log_le_databuf); + if (gfs2_is_jdata(ip)) { + sdp->sd_log_num_jdata++; + gfs2_pin(sdp, bd->bd_bh); + tr->tr_num_databuf_new++; } + sdp->sd_log_num_databuf++; + gfs2_log_lock(sdp); + list_add(&le->le_list, &sdp->sd_log_le_databuf); gfs2_log_unlock(sdp); } @@ -520,7 +521,6 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp) LIST_HEAD(started); struct gfs2_bufdata *bd1 = NULL, *bd2, *bdt; struct buffer_head *bh = NULL,*bh1 = NULL; - unsigned int offset = sizeof(struct gfs2_log_descriptor); struct gfs2_log_descriptor *ld; unsigned int limit; unsigned int total_dbuf = sdp->sd_log_num_databuf; @@ -528,9 +528,7 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp) unsigned int num, n; __be64 *ptr = NULL; - offset += 2*sizeof(__be64) - 1; - offset &= ~(2*sizeof(__be64) - 1); - limit = (sdp->sd_sb.sb_bsize - offset)/sizeof(__be64); + limit = databuf_limit(sdp); /* * Start writing ordered buffers, write journaled buffers @@ -581,10 +579,10 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp) gfs2_log_unlock(sdp); if (!bh) { bh = gfs2_log_get_buf(sdp); - sdp->sd_log_num_hdrs++; ld = (struct gfs2_log_descriptor *) bh->b_data; - ptr = (__be64 *)(bh->b_data + offset); + ptr = (__be64 *)(bh->b_data + + DATABUF_OFFSET); ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC); ld->ld_header.mh_type = @@ -605,7 +603,7 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp) if (unlikely(magic != 0)) set_buffer_escaped(bh1); gfs2_log_lock(sdp); - if (n++ > num) + if (++n >= num) break; } else if (!bh1) { total_dbuf--; @@ -622,6 +620,7 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp) } gfs2_log_unlock(sdp); if (bh) { + set_buffer_mapped(bh); set_buffer_dirty(bh); ll_rw_block(WRITE, 1, &bh); bh = NULL; diff --git a/fs/gfs2/lops.h b/fs/gfs2/lops.h index 965bc65c7c6..41a00df7558 100644 --- a/fs/gfs2/lops.h +++ b/fs/gfs2/lops.h @@ -13,6 +13,13 @@ #include <linux/list.h> #include "incore.h" +#define BUF_OFFSET \ + ((sizeof(struct gfs2_log_descriptor) + sizeof(__be64) - 1) & \ + ~(sizeof(__be64) - 1)) +#define DATABUF_OFFSET \ + ((sizeof(struct gfs2_log_descriptor) + (2 * sizeof(__be64) - 1)) & \ + ~(2 * sizeof(__be64) - 1)) + extern const struct gfs2_log_operations gfs2_glock_lops; extern const struct gfs2_log_operations gfs2_buf_lops; extern const struct gfs2_log_operations gfs2_revoke_lops; @@ -21,6 +28,22 @@ extern const struct gfs2_log_operations gfs2_databuf_lops; extern const struct gfs2_log_operations *gfs2_log_ops[]; +static inline unsigned int buf_limit(struct gfs2_sbd *sdp) +{ + unsigned int limit; + + limit = (sdp->sd_sb.sb_bsize - BUF_OFFSET) / sizeof(__be64); + return limit; +} + +static inline unsigned int databuf_limit(struct gfs2_sbd *sdp) +{ + unsigned int limit; + + limit = (sdp->sd_sb.sb_bsize - DATABUF_OFFSET) / (2 * sizeof(__be64)); + return limit; +} + static inline void lops_init_le(struct gfs2_log_element *le, const struct gfs2_log_operations *lops) { diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index e62d4f620c5..8da343b34ae 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -387,12 +387,18 @@ void gfs2_meta_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen) if (test_clear_buffer_pinned(bh)) { struct gfs2_trans *tr = current->journal_info; + struct gfs2_inode *bh_ip = + GFS2_I(bh->b_page->mapping->host); + gfs2_log_lock(sdp); list_del_init(&bd->bd_le.le_list); gfs2_assert_warn(sdp, sdp->sd_log_num_buf); sdp->sd_log_num_buf--; gfs2_log_unlock(sdp); - tr->tr_num_buf_rm++; + if (bh_ip->i_inode.i_private != NULL) + tr->tr_num_databuf_rm++; + else + tr->tr_num_buf_rm++; brelse(bh); } if (bd) { diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h index e037425bc04..527bf19d969 100644 --- a/fs/gfs2/meta_io.h +++ b/fs/gfs2/meta_io.h @@ -63,7 +63,7 @@ int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, u64 num, static inline int gfs2_meta_inode_buffer(struct gfs2_inode *ip, struct buffer_head **bhp) { - return gfs2_meta_indirect_buffer(ip, 0, ip->i_num.no_addr, 0, bhp); + return gfs2_meta_indirect_buffer(ip, 0, ip->i_no_addr, 0, bhp); } struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen); diff --git a/fs/gfs2/mount.c b/fs/gfs2/mount.c index 4864659555d..6f006a804db 100644 --- a/fs/gfs2/mount.c +++ b/fs/gfs2/mount.c @@ -82,20 +82,19 @@ int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount) char *options, *o, *v; int error = 0; - if (!remount) { - /* If someone preloaded options, use those instead */ - spin_lock(&gfs2_sys_margs_lock); - if (gfs2_sys_margs) { - data = gfs2_sys_margs; - gfs2_sys_margs = NULL; - } - spin_unlock(&gfs2_sys_margs_lock); - - /* Set some defaults */ - args->ar_num_glockd = GFS2_GLOCKD_DEFAULT; - args->ar_quota = GFS2_QUOTA_DEFAULT; - args->ar_data = GFS2_DATA_DEFAULT; + /* If someone preloaded options, use those instead */ + spin_lock(&gfs2_sys_margs_lock); + if (!remount && gfs2_sys_margs) { + data = gfs2_sys_margs; + gfs2_sys_margs = NULL; } + spin_unlock(&gfs2_sys_margs_lock); + + /* Set some defaults */ + memset(args, 0, sizeof(struct gfs2_args)); + args->ar_num_glockd = GFS2_GLOCKD_DEFAULT; + args->ar_quota = GFS2_QUOTA_DEFAULT; + args->ar_data = GFS2_DATA_DEFAULT; /* Split the options into tokens with the "," character and process them */ diff --git a/fs/gfs2/ondisk.c b/fs/gfs2/ondisk.c deleted file mode 100644 index d9ecfd23a49..00000000000 --- a/fs/gfs2/ondisk.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License version 2. - */ - -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/completion.h> -#include <linux/buffer_head.h> - -#include "gfs2.h" -#include <linux/gfs2_ondisk.h> -#include <linux/lm_interface.h> -#include "incore.h" - -#define pv(struct, member, fmt) printk(KERN_INFO " "#member" = "fmt"\n", \ - struct->member); - -/* - * gfs2_xxx_in - read in an xxx struct - * first arg: the cpu-order structure - * buf: the disk-order buffer - * - * gfs2_xxx_out - write out an xxx struct - * first arg: the cpu-order structure - * buf: the disk-order buffer - * - * gfs2_xxx_print - print out an xxx struct - * first arg: the cpu-order structure - */ - -void gfs2_inum_in(struct gfs2_inum_host *no, const void *buf) -{ - const struct gfs2_inum *str = buf; - - no->no_formal_ino = be64_to_cpu(str->no_formal_ino); - no->no_addr = be64_to_cpu(str->no_addr); -} - -void gfs2_inum_out(const struct gfs2_inum_host *no, void *buf) -{ - struct gfs2_inum *str = buf; - - str->no_formal_ino = cpu_to_be64(no->no_formal_ino); - str->no_addr = cpu_to_be64(no->no_addr); -} - -static void gfs2_inum_print(const struct gfs2_inum_host *no) -{ - printk(KERN_INFO " no_formal_ino = %llu\n", (unsigned long long)no->no_formal_ino); - printk(KERN_INFO " no_addr = %llu\n", (unsigned long long)no->no_addr); -} - -static void gfs2_meta_header_in(struct gfs2_meta_header_host *mh, const void *buf) -{ - const struct gfs2_meta_header *str = buf; - - mh->mh_magic = be32_to_cpu(str->mh_magic); - mh->mh_type = be32_to_cpu(str->mh_type); - mh->mh_format = be32_to_cpu(str->mh_format); -} - -void gfs2_sb_in(struct gfs2_sb_host *sb, const void *buf) -{ - const struct gfs2_sb *str = buf; - - gfs2_meta_header_in(&sb->sb_header, buf); - - sb->sb_fs_format = be32_to_cpu(str->sb_fs_format); - sb->sb_multihost_format = be32_to_cpu(str->sb_multihost_format); - sb->sb_bsize = be32_to_cpu(str->sb_bsize); - sb->sb_bsize_shift = be32_to_cpu(str->sb_bsize_shift); - - gfs2_inum_in(&sb->sb_master_dir, (char *)&str->sb_master_dir); - gfs2_inum_in(&sb->sb_root_dir, (char *)&str->sb_root_dir); - - memcpy(sb->sb_lockproto, str->sb_lockproto, GFS2_LOCKNAME_LEN); - memcpy(sb->sb_locktable, str->sb_locktable, GFS2_LOCKNAME_LEN); -} - -void gfs2_rindex_in(struct gfs2_rindex_host *ri, const void *buf) -{ - const struct gfs2_rindex *str = buf; - - ri->ri_addr = be64_to_cpu(str->ri_addr); - ri->ri_length = be32_to_cpu(str->ri_length); - ri->ri_data0 = be64_to_cpu(str->ri_data0); - ri->ri_data = be32_to_cpu(str->ri_data); - ri->ri_bitbytes = be32_to_cpu(str->ri_bitbytes); - -} - -void gfs2_rindex_print(const struct gfs2_rindex_host *ri) -{ - printk(KERN_INFO " ri_addr = %llu\n", (unsigned long long)ri->ri_addr); - pv(ri, ri_length, "%u"); - - printk(KERN_INFO " ri_data0 = %llu\n", (unsigned long long)ri->ri_data0); - pv(ri, ri_data, "%u"); - - pv(ri, ri_bitbytes, "%u"); -} - -void gfs2_rgrp_in(struct gfs2_rgrp_host *rg, const void *buf) -{ - const struct gfs2_rgrp *str = buf; - - rg->rg_flags = be32_to_cpu(str->rg_flags); - rg->rg_free = be32_to_cpu(str->rg_free); - rg->rg_dinodes = be32_to_cpu(str->rg_dinodes); - rg->rg_igeneration = be64_to_cpu(str->rg_igeneration); -} - -void gfs2_rgrp_out(const struct gfs2_rgrp_host *rg, void *buf) -{ - struct gfs2_rgrp *str = buf; - - str->rg_flags = cpu_to_be32(rg->rg_flags); - str->rg_free = cpu_to_be32(rg->rg_free); - str->rg_dinodes = cpu_to_be32(rg->rg_dinodes); - str->__pad = cpu_to_be32(0); - str->rg_igeneration = cpu_to_be64(rg->rg_igeneration); - memset(&str->rg_reserved, 0, sizeof(str->rg_reserved)); -} - -void gfs2_quota_in(struct gfs2_quota_host *qu, const void *buf) -{ - const struct gfs2_quota *str = buf; - - qu->qu_limit = be64_to_cpu(str->qu_limit); - qu->qu_warn = be64_to_cpu(str->qu_warn); - qu->qu_value = be64_to_cpu(str->qu_value); -} - -void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf) -{ - const struct gfs2_dinode_host *di = &ip->i_di; - struct gfs2_dinode *str = buf; - - str->di_header.mh_magic = cpu_to_be32(GFS2_MAGIC); - str->di_header.mh_type = cpu_to_be32(GFS2_METATYPE_DI); - str->di_header.__pad0 = 0; - str->di_header.mh_format = cpu_to_be32(GFS2_FORMAT_DI); - str->di_header.__pad1 = 0; - - gfs2_inum_out(&ip->i_num, &str->di_num); - - str->di_mode = cpu_to_be32(ip->i_inode.i_mode); - str->di_uid = cpu_to_be32(ip->i_inode.i_uid); - str->di_gid = cpu_to_be32(ip->i_inode.i_gid); - str->di_nlink = cpu_to_be32(ip->i_inode.i_nlink); - str->di_size = cpu_to_be64(di->di_size); - str->di_blocks = cpu_to_be64(di->di_blocks); - str->di_atime = cpu_to_be64(ip->i_inode.i_atime.tv_sec); - str->di_mtime = cpu_to_be64(ip->i_inode.i_mtime.tv_sec); - str->di_ctime = cpu_to_be64(ip->i_inode.i_ctime.tv_sec); - - str->di_goal_meta = cpu_to_be64(di->di_goal_meta); - str->di_goal_data = cpu_to_be64(di->di_goal_data); - str->di_generation = cpu_to_be64(di->di_generation); - - str->di_flags = cpu_to_be32(di->di_flags); - str->di_height = cpu_to_be16(di->di_height); - str->di_payload_format = cpu_to_be32(S_ISDIR(ip->i_inode.i_mode) && - !(ip->i_di.di_flags & GFS2_DIF_EXHASH) ? - GFS2_FORMAT_DE : 0); - str->di_depth = cpu_to_be16(di->di_depth); - str->di_entries = cpu_to_be32(di->di_entries); - - str->di_eattr = cpu_to_be64(di->di_eattr); -} - -void gfs2_dinode_print(const struct gfs2_inode *ip) -{ - const struct gfs2_dinode_host *di = &ip->i_di; - - gfs2_inum_print(&ip->i_num); - - printk(KERN_INFO " di_size = %llu\n", (unsigned long long)di->di_size); - printk(KERN_INFO " di_blocks = %llu\n", (unsigned long long)di->di_blocks); - printk(KERN_INFO " di_goal_meta = %llu\n", (unsigned long long)di->di_goal_meta); - printk(KERN_INFO " di_goal_data = %llu\n", (unsigned long long)di->di_goal_data); - - pv(di, di_flags, "0x%.8X"); - pv(di, di_height, "%u"); - - pv(di, di_depth, "%u"); - pv(di, di_entries, "%u"); - - printk(KERN_INFO " di_eattr = %llu\n", (unsigned long long)di->di_eattr); -} - -void gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf) -{ - const struct gfs2_log_header *str = buf; - - gfs2_meta_header_in(&lh->lh_header, buf); - lh->lh_sequence = be64_to_cpu(str->lh_sequence); - lh->lh_flags = be32_to_cpu(str->lh_flags); - lh->lh_tail = be32_to_cpu(str->lh_tail); - lh->lh_blkno = be32_to_cpu(str->lh_blkno); - lh->lh_hash = be32_to_cpu(str->lh_hash); -} - -void gfs2_inum_range_in(struct gfs2_inum_range_host *ir, const void *buf) -{ - const struct gfs2_inum_range *str = buf; - - ir->ir_start = be64_to_cpu(str->ir_start); - ir->ir_length = be64_to_cpu(str->ir_length); -} - -void gfs2_inum_range_out(const struct gfs2_inum_range_host *ir, void *buf) -{ - struct gfs2_inum_range *str = buf; - - str->ir_start = cpu_to_be64(ir->ir_start); - str->ir_length = cpu_to_be64(ir->ir_length); -} - -void gfs2_statfs_change_in(struct gfs2_statfs_change_host *sc, const void *buf) -{ - const struct gfs2_statfs_change *str = buf; - - sc->sc_total = be64_to_cpu(str->sc_total); - sc->sc_free = be64_to_cpu(str->sc_free); - sc->sc_dinodes = be64_to_cpu(str->sc_dinodes); -} - -void gfs2_statfs_change_out(const struct gfs2_statfs_change_host *sc, void *buf) -{ - struct gfs2_statfs_change *str = buf; - - str->sc_total = cpu_to_be64(sc->sc_total); - str->sc_free = cpu_to_be64(sc->sc_free); - str->sc_dinodes = cpu_to_be64(sc->sc_dinodes); -} - -void gfs2_quota_change_in(struct gfs2_quota_change_host *qc, const void *buf) -{ - const struct gfs2_quota_change *str = buf; - - qc->qc_change = be64_to_cpu(str->qc_change); - qc->qc_flags = be32_to_cpu(str->qc_flags); - qc->qc_id = be32_to_cpu(str->qc_id); -} - diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c index 30c15622174..26c888890c2 100644 --- a/fs/gfs2/ops_address.c +++ b/fs/gfs2/ops_address.c @@ -1,6 +1,6 @@ /* * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions @@ -32,6 +32,7 @@ #include "trans.h" #include "rgrp.h" #include "ops_file.h" +#include "super.h" #include "util.h" #include "glops.h" @@ -49,6 +50,8 @@ static void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, end = start + bsize; if (end <= from || start >= to) continue; + if (gfs2_is_jdata(ip)) + set_buffer_uptodate(bh); gfs2_trans_add_bh(ip->i_gl, bh, 0); } } @@ -134,7 +137,9 @@ static int gfs2_writepage(struct page *page, struct writeback_control *wbc) return 0; /* don't care */ } - if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) { + if ((sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) && + PageChecked(page)) { + ClearPageChecked(page); error = gfs2_trans_begin(sdp, RES_DINODE + 1, 0); if (error) goto out_ignore; @@ -203,11 +208,7 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page) * so we need to supply one here. It doesn't happen often. */ if (unlikely(page->index)) { - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr, 0, PAGE_CACHE_SIZE); - kunmap_atomic(kaddr, KM_USER0); - flush_dcache_page(page); - SetPageUptodate(page); + zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0); return 0; } @@ -450,6 +451,31 @@ out_uninit: } /** + * adjust_fs_space - Adjusts the free space available due to gfs2_grow + * @inode: the rindex inode + */ +static void adjust_fs_space(struct inode *inode) +{ + struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; + struct gfs2_statfs_change_host *m_sc = &sdp->sd_statfs_master; + struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local; + u64 fs_total, new_free; + + /* Total up the file system space, according to the latest rindex. */ + fs_total = gfs2_ri_total(sdp); + + spin_lock(&sdp->sd_statfs_spin); + if (fs_total > (m_sc->sc_total + l_sc->sc_total)) + new_free = fs_total - (m_sc->sc_total + l_sc->sc_total); + else + new_free = 0; + spin_unlock(&sdp->sd_statfs_spin); + fs_warn(sdp, "File system extended by %llu blocks.\n", + (unsigned long long)new_free); + gfs2_statfs_change(sdp, new_free, new_free, 0); +} + +/** * gfs2_commit_write - Commit write to a file * @file: The file to write to * @page: The page containing the data @@ -511,6 +537,9 @@ static int gfs2_commit_write(struct file *file, struct page *page, di->di_size = cpu_to_be64(inode->i_size); } + if (inode == sdp->sd_rindex) + adjust_fs_space(inode); + brelse(dibh); gfs2_trans_end(sdp); if (al->al_requested) { @@ -543,6 +572,23 @@ fail_nounlock: } /** + * gfs2_set_page_dirty - Page dirtying function + * @page: The page to dirty + * + * Returns: 1 if it dirtyed the page, or 0 otherwise + */ + +static int gfs2_set_page_dirty(struct page *page) +{ + struct gfs2_inode *ip = GFS2_I(page->mapping->host); + struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host); + + if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) + SetPageChecked(page); + return __set_page_dirty_buffers(page); +} + +/** * gfs2_bmap - Block map function * @mapping: Address space info * @lblock: The block to map @@ -578,6 +624,8 @@ static void discard_buffer(struct gfs2_sbd *sdp, struct buffer_head *bh) if (bd) { bd->bd_bh = NULL; bh->b_private = NULL; + if (!bd->bd_ail && list_empty(&bd->bd_le.le_list)) + kmem_cache_free(gfs2_bufdata_cachep, bd); } gfs2_log_unlock(sdp); @@ -598,6 +646,8 @@ static void gfs2_invalidatepage(struct page *page, unsigned long offset) unsigned int curr_off = 0; BUG_ON(!PageLocked(page)); + if (offset == 0) + ClearPageChecked(page); if (!page_has_buffers(page)) return; @@ -728,8 +778,8 @@ static unsigned limit = 0; return; fs_warn(sdp, "ip = %llu %llu\n", - (unsigned long long)ip->i_num.no_formal_ino, - (unsigned long long)ip->i_num.no_addr); + (unsigned long long)ip->i_no_formal_ino, + (unsigned long long)ip->i_no_addr); for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) fs_warn(sdp, "ip->i_cache[%u] = %s\n", @@ -810,6 +860,7 @@ const struct address_space_operations gfs2_file_aops = { .sync_page = block_sync_page, .prepare_write = gfs2_prepare_write, .commit_write = gfs2_commit_write, + .set_page_dirty = gfs2_set_page_dirty, .bmap = gfs2_bmap, .invalidatepage = gfs2_invalidatepage, .releasepage = gfs2_releasepage, diff --git a/fs/gfs2/ops_address.h b/fs/gfs2/ops_address.h index 35aaee4aa7e..fa1b5b3d28b 100644 --- a/fs/gfs2/ops_address.h +++ b/fs/gfs2/ops_address.h @@ -1,6 +1,6 @@ /* * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions diff --git a/fs/gfs2/ops_dentry.c b/fs/gfs2/ops_dentry.c index a6fdc52f554..793e334d098 100644 --- a/fs/gfs2/ops_dentry.c +++ b/fs/gfs2/ops_dentry.c @@ -21,6 +21,7 @@ #include "glock.h" #include "ops_dentry.h" #include "util.h" +#include "inode.h" /** * gfs2_drevalidate - Check directory lookup consistency @@ -40,14 +41,15 @@ static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) struct gfs2_inode *dip = GFS2_I(parent->d_inode); struct inode *inode = dentry->d_inode; struct gfs2_holder d_gh; - struct gfs2_inode *ip; - struct gfs2_inum_host inum; - unsigned int type; + struct gfs2_inode *ip = NULL; int error; int had_lock=0; - if (inode && is_bad_inode(inode)) - goto invalid; + if (inode) { + if (is_bad_inode(inode)) + goto invalid; + ip = GFS2_I(inode); + } if (sdp->sd_args.ar_localcaching) goto valid; @@ -59,7 +61,7 @@ static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) goto fail; } - error = gfs2_dir_search(parent->d_inode, &dentry->d_name, &inum, &type); + error = gfs2_dir_check(parent->d_inode, &dentry->d_name, ip); switch (error) { case 0: if (!inode) @@ -73,16 +75,6 @@ static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) goto fail_gunlock; } - ip = GFS2_I(inode); - - if (!gfs2_inum_equal(&ip->i_num, &inum)) - goto invalid_gunlock; - - if (IF2DT(ip->i_inode.i_mode) != type) { - gfs2_consist_inode(dip); - goto fail_gunlock; - } - valid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c index aad918337a4..99ea5659bc2 100644 --- a/fs/gfs2/ops_export.c +++ b/fs/gfs2/ops_export.c @@ -22,10 +22,14 @@ #include "glops.h" #include "inode.h" #include "ops_dentry.h" -#include "ops_export.h" +#include "ops_fstype.h" #include "rgrp.h" #include "util.h" +#define GFS2_SMALL_FH_SIZE 4 +#define GFS2_LARGE_FH_SIZE 8 +#define GFS2_OLD_FH_SIZE 10 + static struct dentry *gfs2_decode_fh(struct super_block *sb, __u32 *p, int fh_len, @@ -35,31 +39,28 @@ static struct dentry *gfs2_decode_fh(struct super_block *sb, void *context) { __be32 *fh = (__force __be32 *)p; - struct gfs2_fh_obj fh_obj; - struct gfs2_inum_host *this, parent; + struct gfs2_inum_host inum, parent; - this = &fh_obj.this; - fh_obj.imode = DT_UNKNOWN; memset(&parent, 0, sizeof(struct gfs2_inum)); switch (fh_len) { case GFS2_LARGE_FH_SIZE: + case GFS2_OLD_FH_SIZE: parent.no_formal_ino = ((u64)be32_to_cpu(fh[4])) << 32; parent.no_formal_ino |= be32_to_cpu(fh[5]); parent.no_addr = ((u64)be32_to_cpu(fh[6])) << 32; parent.no_addr |= be32_to_cpu(fh[7]); - fh_obj.imode = be32_to_cpu(fh[8]); case GFS2_SMALL_FH_SIZE: - this->no_formal_ino = ((u64)be32_to_cpu(fh[0])) << 32; - this->no_formal_ino |= be32_to_cpu(fh[1]); - this->no_addr = ((u64)be32_to_cpu(fh[2])) << 32; - this->no_addr |= be32_to_cpu(fh[3]); + inum.no_formal_ino = ((u64)be32_to_cpu(fh[0])) << 32; + inum.no_formal_ino |= be32_to_cpu(fh[1]); + inum.no_addr = ((u64)be32_to_cpu(fh[2])) << 32; + inum.no_addr |= be32_to_cpu(fh[3]); break; default: return NULL; } - return gfs2_export_ops.find_exported_dentry(sb, &fh_obj, &parent, + return gfs2_export_ops.find_exported_dentry(sb, &inum, &parent, acceptable, context); } @@ -75,10 +76,10 @@ static int gfs2_encode_fh(struct dentry *dentry, __u32 *p, int *len, (connectable && *len < GFS2_LARGE_FH_SIZE)) return 255; - fh[0] = cpu_to_be32(ip->i_num.no_formal_ino >> 32); - fh[1] = cpu_to_be32(ip->i_num.no_formal_ino & 0xFFFFFFFF); - fh[2] = cpu_to_be32(ip->i_num.no_addr >> 32); - fh[3] = cpu_to_be32(ip->i_num.no_addr & 0xFFFFFFFF); + fh[0] = cpu_to_be32(ip->i_no_formal_ino >> 32); + fh[1] = cpu_to_be32(ip->i_no_formal_ino & 0xFFFFFFFF); + fh[2] = cpu_to_be32(ip->i_no_addr >> 32); + fh[3] = cpu_to_be32(ip->i_no_addr & 0xFFFFFFFF); *len = GFS2_SMALL_FH_SIZE; if (!connectable || inode == sb->s_root->d_inode) @@ -90,13 +91,10 @@ static int gfs2_encode_fh(struct dentry *dentry, __u32 *p, int *len, igrab(inode); spin_unlock(&dentry->d_lock); - fh[4] = cpu_to_be32(ip->i_num.no_formal_ino >> 32); - fh[5] = cpu_to_be32(ip->i_num.no_formal_ino & 0xFFFFFFFF); - fh[6] = cpu_to_be32(ip->i_num.no_addr >> 32); - fh[7] = cpu_to_be32(ip->i_num.no_addr & 0xFFFFFFFF); - - fh[8] = cpu_to_be32(inode->i_mode); - fh[9] = 0; /* pad to double word */ + fh[4] = cpu_to_be32(ip->i_no_formal_ino >> 32); + fh[5] = cpu_to_be32(ip->i_no_formal_ino & 0xFFFFFFFF); + fh[6] = cpu_to_be32(ip->i_no_addr >> 32); + fh[7] = cpu_to_be32(ip->i_no_addr & 0xFFFFFFFF); *len = GFS2_LARGE_FH_SIZE; iput(inode); @@ -144,7 +142,8 @@ static int gfs2_get_name(struct dentry *parent, char *name, ip = GFS2_I(inode); *name = 0; - gnfd.inum = ip->i_num; + gnfd.inum.no_addr = ip->i_no_addr; + gnfd.inum.no_formal_ino = ip->i_no_formal_ino; gnfd.name = name; error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &gh); @@ -192,8 +191,7 @@ static struct dentry *gfs2_get_parent(struct dentry *child) static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj) { struct gfs2_sbd *sdp = sb->s_fs_info; - struct gfs2_fh_obj *fh_obj = (struct gfs2_fh_obj *)inum_obj; - struct gfs2_inum_host *inum = &fh_obj->this; + struct gfs2_inum_host *inum = inum_obj; struct gfs2_holder i_gh, ri_gh, rgd_gh; struct gfs2_rgrpd *rgd; struct inode *inode; @@ -202,9 +200,9 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj) /* System files? */ - inode = gfs2_ilookup(sb, inum); + inode = gfs2_ilookup(sb, inum->no_addr); if (inode) { - if (GFS2_I(inode)->i_num.no_formal_ino != inum->no_formal_ino) { + if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) { iput(inode); return ERR_PTR(-ESTALE); } @@ -236,7 +234,9 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj) gfs2_glock_dq_uninit(&rgd_gh); gfs2_glock_dq_uninit(&ri_gh); - inode = gfs2_inode_lookup(sb, inum, fh_obj->imode); + inode = gfs2_inode_lookup(sb, DT_UNKNOWN, + inum->no_addr, + 0); if (!inode) goto fail; if (IS_ERR(inode)) { @@ -250,6 +250,15 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj) goto fail; } + /* Pick up the works we bypass in gfs2_inode_lookup */ + if (inode->i_state & I_NEW) + gfs2_set_iop(inode); + + if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) { + iput(inode); + goto fail; + } + error = -EIO; if (GFS2_I(inode)->i_di.di_flags & GFS2_DIF_SYSTEM) { iput(inode); diff --git a/fs/gfs2/ops_export.h b/fs/gfs2/ops_export.h deleted file mode 100644 index f925a955b3b..00000000000 --- a/fs/gfs2/ops_export.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License version 2. - */ - -#ifndef __OPS_EXPORT_DOT_H__ -#define __OPS_EXPORT_DOT_H__ - -#define GFS2_SMALL_FH_SIZE 4 -#define GFS2_LARGE_FH_SIZE 10 - -extern struct export_operations gfs2_export_ops; -struct gfs2_fh_obj { - struct gfs2_inum_host this; - __u32 imode; -}; - -#endif /* __OPS_EXPORT_DOT_H__ */ diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index 064df880458..196d83266e3 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -502,7 +502,7 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl) struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); struct gfs2_sbd *sdp = GFS2_SB(file->f_mapping->host); struct lm_lockname name = - { .ln_number = ip->i_num.no_addr, + { .ln_number = ip->i_no_addr, .ln_type = LM_TYPE_PLOCK }; if (!(fl->fl_flags & FL_POSIX)) @@ -557,7 +557,7 @@ static int do_flock(struct file *file, int cmd, struct file_lock *fl) gfs2_glock_dq_uninit(fl_gh); } else { error = gfs2_glock_get(GFS2_SB(&ip->i_inode), - ip->i_num.no_addr, &gfs2_flock_glops, + ip->i_no_addr, &gfs2_flock_glops, CREATE, &gl); if (error) goto out; @@ -635,7 +635,6 @@ const struct file_operations gfs2_file_fops = { .release = gfs2_close, .fsync = gfs2_fsync, .lock = gfs2_lock, - .sendfile = generic_file_sendfile, .flock = gfs2_flock, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 2c5f8e7def0..cf5aa505054 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -27,7 +27,6 @@ #include "inode.h" #include "lm.h" #include "mount.h" -#include "ops_export.h" #include "ops_fstype.h" #include "ops_super.h" #include "recovery.h" @@ -105,6 +104,7 @@ static void init_vfs(struct super_block *sb, unsigned noatime) sb->s_magic = GFS2_MAGIC; sb->s_op = &gfs2_super_ops; sb->s_export_op = &gfs2_export_ops; + sb->s_time_gran = 1; sb->s_maxbytes = MAX_LFS_FILESIZE; if (sb->s_flags & (MS_NOATIME | MS_NODIRATIME)) @@ -116,7 +116,6 @@ static void init_vfs(struct super_block *sb, unsigned noatime) static int init_names(struct gfs2_sbd *sdp, int silent) { - struct page *page; char *proto, *table; int error = 0; @@ -126,14 +125,9 @@ static int init_names(struct gfs2_sbd *sdp, int silent) /* Try to autodetect */ if (!proto[0] || !table[0]) { - struct gfs2_sb *sb; - page = gfs2_read_super(sdp->sd_vfs, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift); - if (!page) - return -ENOBUFS; - sb = kmap(page); - gfs2_sb_in(&sdp->sd_sb, sb); - kunmap(page); - __free_page(page); + error = gfs2_read_super(sdp, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift); + if (error) + return error; error = gfs2_check_sb(sdp, &sdp->sd_sb, silent); if (error) @@ -151,6 +145,9 @@ static int init_names(struct gfs2_sbd *sdp, int silent) snprintf(sdp->sd_proto_name, GFS2_FSNAME_LEN, "%s", proto); snprintf(sdp->sd_table_name, GFS2_FSNAME_LEN, "%s", table); + while ((table = strchr(sdp->sd_table_name, '/'))) + *table = '_'; + out: return error; } @@ -236,17 +233,17 @@ fail: return error; } -static struct inode *gfs2_lookup_root(struct super_block *sb, - struct gfs2_inum_host *inum) +static inline struct inode *gfs2_lookup_root(struct super_block *sb, + u64 no_addr) { - return gfs2_inode_lookup(sb, inum, DT_DIR); + return gfs2_inode_lookup(sb, DT_DIR, no_addr, 0); } static int init_sb(struct gfs2_sbd *sdp, int silent, int undo) { struct super_block *sb = sdp->sd_vfs; struct gfs2_holder sb_gh; - struct gfs2_inum_host *inum; + u64 no_addr; struct inode *inode; int error = 0; @@ -289,10 +286,10 @@ static int init_sb(struct gfs2_sbd *sdp, int silent, int undo) sb_set_blocksize(sb, sdp->sd_sb.sb_bsize); /* Get the root inode */ - inum = &sdp->sd_sb.sb_root_dir; + no_addr = sdp->sd_sb.sb_root_dir.no_addr; if (sb->s_type == &gfs2meta_fs_type) - inum = &sdp->sd_sb.sb_master_dir; - inode = gfs2_lookup_root(sb, inum); + no_addr = sdp->sd_sb.sb_master_dir.no_addr; + inode = gfs2_lookup_root(sb, no_addr); if (IS_ERR(inode)) { error = PTR_ERR(inode); fs_err(sdp, "can't read in root inode: %d\n", error); @@ -449,7 +446,7 @@ static int init_inodes(struct gfs2_sbd *sdp, int undo) if (undo) goto fail_qinode; - inode = gfs2_lookup_root(sdp->sd_vfs, &sdp->sd_sb.sb_master_dir); + inode = gfs2_lookup_root(sdp->sd_vfs, sdp->sd_sb.sb_master_dir.no_addr); if (IS_ERR(inode)) { error = PTR_ERR(inode); fs_err(sdp, "can't read in master directory: %d\n", error); diff --git a/fs/gfs2/ops_fstype.h b/fs/gfs2/ops_fstype.h index 7cc2c296271..407029b3b2b 100644 --- a/fs/gfs2/ops_fstype.h +++ b/fs/gfs2/ops_fstype.h @@ -14,5 +14,6 @@ extern struct file_system_type gfs2_fs_type; extern struct file_system_type gfs2meta_fs_type; +extern struct export_operations gfs2_export_ops; #endif /* __OPS_FSTYPE_DOT_H__ */ diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index d85f6e05cb9..911c115b5c6 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -157,7 +157,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, if (error) goto out_gunlock; - error = gfs2_dir_search(dir, &dentry->d_name, NULL, NULL); + error = gfs2_dir_check(dir, &dentry->d_name, NULL); switch (error) { case -ENOENT: break; @@ -206,7 +206,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, goto out_gunlock_q; error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_ri.ri_length + + al->al_rgd->rd_length + 2 * RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) @@ -217,8 +217,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, goto out_ipres; } - error = gfs2_dir_add(dir, &dentry->d_name, &ip->i_num, - IF2DT(inode->i_mode)); + error = gfs2_dir_add(dir, &dentry->d_name, ip, IF2DT(inode->i_mode)); if (error) goto out_end_trans; @@ -275,7 +274,7 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); - rgd = gfs2_blk2rgrpd(sdp, ip->i_num.no_addr); + rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr); gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2); @@ -420,7 +419,7 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1)); gfs2_qstr2dirent(&str, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent); - gfs2_inum_out(&dip->i_num, &dent->de_inum); + gfs2_inum_out(dip, dent); dent->de_type = cpu_to_be16(DT_DIR); gfs2_dinode_out(ip, di); @@ -472,7 +471,7 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry) gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); - rgd = gfs2_blk2rgrpd(sdp, ip->i_num.no_addr); + rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr); gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2); error = gfs2_glock_nq_m(3, ghs); @@ -614,7 +613,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, * this is the case of the target file already existing * so we unlink before doing the rename */ - nrgd = gfs2_blk2rgrpd(sdp, nip->i_num.no_addr); + nrgd = gfs2_blk2rgrpd(sdp, nip->i_no_addr); if (nrgd) gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++); } @@ -653,7 +652,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (error) goto out_gunlock; - error = gfs2_dir_search(ndir, &ndentry->d_name, NULL, NULL); + error = gfs2_dir_check(ndir, &ndentry->d_name, NULL); switch (error) { case -ENOENT: error = 0; @@ -712,7 +711,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, goto out_gunlock_q; error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_ri.ri_length + + al->al_rgd->rd_length + 4 * RES_DINODE + 4 * RES_LEAF + RES_STATFS + RES_QUOTA + 4, 0); if (error) @@ -750,7 +749,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (error) goto out_end_trans; - error = gfs2_dir_mvino(ip, &name, &ndip->i_num, DT_DIR); + error = gfs2_dir_mvino(ip, &name, ndip, DT_DIR); if (error) goto out_end_trans; } else { @@ -758,7 +757,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, error = gfs2_meta_inode_buffer(ip, &dibh); if (error) goto out_end_trans; - ip->i_inode.i_ctime = CURRENT_TIME_SEC; + ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); @@ -768,8 +767,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (error) goto out_end_trans; - error = gfs2_dir_add(ndir, &ndentry->d_name, &ip->i_num, - IF2DT(ip->i_inode.i_mode)); + error = gfs2_dir_add(ndir, &ndentry->d_name, ip, IF2DT(ip->i_inode.i_mode)); if (error) goto out_end_trans; @@ -905,8 +903,8 @@ static int setattr_size(struct inode *inode, struct iattr *attr) } error = gfs2_truncatei(ip, attr->ia_size); - if (error) - return error; + if (error && (inode->i_size != ip->i_di.di_size)) + i_size_write(inode, ip->i_di.di_size); return error; } diff --git a/fs/gfs2/ops_super.c b/fs/gfs2/ops_super.c index 485ce3d4992..603d940f115 100644 --- a/fs/gfs2/ops_super.c +++ b/fs/gfs2/ops_super.c @@ -326,8 +326,10 @@ static void gfs2_clear_inode(struct inode *inode) gfs2_glock_schedule_for_reclaim(ip->i_gl); gfs2_glock_put(ip->i_gl); ip->i_gl = NULL; - if (ip->i_iopen_gh.gh_gl) + if (ip->i_iopen_gh.gh_gl) { + ip->i_iopen_gh.gh_gl->gl_object = NULL; gfs2_glock_dq_uninit(&ip->i_iopen_gh); + } } } @@ -422,13 +424,13 @@ static void gfs2_delete_inode(struct inode *inode) if (!inode->i_private) goto out; - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB, &gh); + error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); if (unlikely(error)) { gfs2_glock_dq_uninit(&ip->i_iopen_gh); goto out; } - gfs2_glock_dq(&ip->i_iopen_gh); + gfs2_glock_dq_wait(&ip->i_iopen_gh); gfs2_holder_reinit(LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB | GL_NOCACHE, &ip->i_iopen_gh); error = gfs2_glock_nq(&ip->i_iopen_gh); if (error) diff --git a/fs/gfs2/ops_vm.c b/fs/gfs2/ops_vm.c index aa0dbd2aac1..404b7cc9f8c 100644 --- a/fs/gfs2/ops_vm.c +++ b/fs/gfs2/ops_vm.c @@ -66,7 +66,7 @@ static int alloc_page_backing(struct gfs2_inode *ip, struct page *page) if (error) goto out_gunlock_q; - error = gfs2_trans_begin(sdp, al->al_rgd->rd_ri.ri_length + + error = gfs2_trans_begin(sdp, al->al_rgd->rd_length + ind_blocks + RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index c186857e48a..6e546ee8f3d 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -66,6 +66,18 @@ #define QUOTA_USER 1 #define QUOTA_GROUP 0 +struct gfs2_quota_host { + u64 qu_limit; + u64 qu_warn; + s64 qu_value; +}; + +struct gfs2_quota_change_host { + u64 qc_change; + u32 qc_flags; /* GFS2_QCF_... */ + u32 qc_id; +}; + static u64 qd2offset(struct gfs2_quota_data *qd) { u64 offset; @@ -561,6 +573,25 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) mutex_unlock(&sdp->sd_quota_mutex); } +static void gfs2_quota_in(struct gfs2_quota_host *qu, const void *buf) +{ + const struct gfs2_quota *str = buf; + + qu->qu_limit = be64_to_cpu(str->qu_limit); + qu->qu_warn = be64_to_cpu(str->qu_warn); + qu->qu_value = be64_to_cpu(str->qu_value); +} + +static void gfs2_quota_out(const struct gfs2_quota_host *qu, void *buf) +{ + struct gfs2_quota *str = buf; + + str->qu_limit = cpu_to_be64(qu->qu_limit); + str->qu_warn = cpu_to_be64(qu->qu_warn); + str->qu_value = cpu_to_be64(qu->qu_value); + memset(&str->qu_reserved, 0, sizeof(str->qu_reserved)); +} + /** * gfs2_adjust_quota * @@ -573,12 +604,13 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, struct inode *inode = &ip->i_inode; struct address_space *mapping = inode->i_mapping; unsigned long index = loc >> PAGE_CACHE_SHIFT; - unsigned offset = loc & (PAGE_CACHE_SHIFT - 1); + unsigned offset = loc & (PAGE_CACHE_SIZE - 1); unsigned blocksize, iblock, pos; struct buffer_head *bh; struct page *page; void *kaddr; - __be64 *ptr; + char *ptr; + struct gfs2_quota_host qp; s64 value; int err = -EIO; @@ -620,13 +652,17 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, kaddr = kmap_atomic(page, KM_USER0); ptr = kaddr + offset; - value = (s64)be64_to_cpu(*ptr) + change; - *ptr = cpu_to_be64(value); + gfs2_quota_in(&qp, ptr); + qp.qu_value += change; + value = qp.qu_value; + gfs2_quota_out(&qp, ptr); flush_dcache_page(page); kunmap_atomic(kaddr, KM_USER0); err = 0; qd->qd_qb.qb_magic = cpu_to_be32(GFS2_MAGIC); qd->qd_qb.qb_value = cpu_to_be64(value); + ((struct gfs2_quota_lvb*)(qd->qd_gl->gl_lvb))->qb_magic = cpu_to_be32(GFS2_MAGIC); + ((struct gfs2_quota_lvb*)(qd->qd_gl->gl_lvb))->qb_value = cpu_to_be64(value); unlock: unlock_page(page); page_cache_release(page); @@ -689,7 +725,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) goto out_alloc; error = gfs2_trans_begin(sdp, - al->al_rgd->rd_ri.ri_length + + al->al_rgd->rd_length + num_qd * data_blocks + nalloc * ind_blocks + RES_DINODE + num_qd + @@ -709,7 +745,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) offset = qd2offset(qd); error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync, (struct gfs2_quota_data *) - qd->qd_gl->gl_lvb); + qd); if (error) goto out_end_trans; @@ -1050,6 +1086,15 @@ int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id) return error; } +static void gfs2_quota_change_in(struct gfs2_quota_change_host *qc, const void *buf) +{ + const struct gfs2_quota_change *str = buf; + + qc->qc_change = be64_to_cpu(str->qc_change); + qc->qc_flags = be32_to_cpu(str->qc_flags); + qc->qc_id = be32_to_cpu(str->qc_id); +} + int gfs2_quota_init(struct gfs2_sbd *sdp) { struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode); diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 8bc182c7e2e..5ada38c99a2 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -116,6 +116,22 @@ void gfs2_revoke_clean(struct gfs2_sbd *sdp) } } +static int gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf) +{ + const struct gfs2_log_header *str = buf; + + if (str->lh_header.mh_magic != cpu_to_be32(GFS2_MAGIC) || + str->lh_header.mh_type != cpu_to_be32(GFS2_METATYPE_LH)) + return 1; + + lh->lh_sequence = be64_to_cpu(str->lh_sequence); + lh->lh_flags = be32_to_cpu(str->lh_flags); + lh->lh_tail = be32_to_cpu(str->lh_tail); + lh->lh_blkno = be32_to_cpu(str->lh_blkno); + lh->lh_hash = be32_to_cpu(str->lh_hash); + return 0; +} + /** * get_log_header - read the log header for a given segment * @jd: the journal @@ -147,12 +163,10 @@ static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk, sizeof(u32)); hash = crc32_le(hash, (unsigned char const *)¬hing, sizeof(nothing)); hash ^= (u32)~0; - gfs2_log_header_in(&lh, bh->b_data); + error = gfs2_log_header_in(&lh, bh->b_data); brelse(bh); - if (lh.lh_header.mh_magic != GFS2_MAGIC || - lh.lh_header.mh_type != GFS2_METATYPE_LH || - lh.lh_blkno != blk || lh.lh_hash != hash) + if (error || lh.lh_blkno != blk || lh.lh_hash != hash) return 1; *head = lh; diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 1727f5012ef..e4e04062515 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1,6 +1,6 @@ /* * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions @@ -28,6 +28,7 @@ #include "ops_file.h" #include "util.h" #include "log.h" +#include "inode.h" #define BFITNOENT ((u32)~0) @@ -50,6 +51,9 @@ static const char valid_change[16] = { 1, 0, 0, 0 }; +static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, + unsigned char old_state, unsigned char new_state); + /** * gfs2_setbit - Set a bit in the bitmaps * @buffer: the buffer that holds the bitmaps @@ -204,7 +208,7 @@ void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd) { struct gfs2_sbd *sdp = rgd->rd_sbd; struct gfs2_bitmap *bi = NULL; - u32 length = rgd->rd_ri.ri_length; + u32 length = rgd->rd_length; u32 count[4], tmp; int buf, x; @@ -227,7 +231,7 @@ void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd) return; } - tmp = rgd->rd_ri.ri_data - + tmp = rgd->rd_data - rgd->rd_rg.rg_free - rgd->rd_rg.rg_dinodes; if (count[1] + count[2] != tmp) { @@ -253,10 +257,10 @@ void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd) } -static inline int rgrp_contains_block(struct gfs2_rindex_host *ri, u64 block) +static inline int rgrp_contains_block(struct gfs2_rgrpd *rgd, u64 block) { - u64 first = ri->ri_data0; - u64 last = first + ri->ri_data; + u64 first = rgd->rd_data0; + u64 last = first + rgd->rd_data; return first <= block && block < last; } @@ -275,7 +279,7 @@ struct gfs2_rgrpd *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, u64 blk) spin_lock(&sdp->sd_rindex_spin); list_for_each_entry(rgd, &sdp->sd_rindex_mru_list, rd_list_mru) { - if (rgrp_contains_block(&rgd->rd_ri, blk)) { + if (rgrp_contains_block(rgd, blk)) { list_move(&rgd->rd_list_mru, &sdp->sd_rindex_mru_list); spin_unlock(&sdp->sd_rindex_spin); return rgd; @@ -354,6 +358,15 @@ void gfs2_clear_rgrpd(struct gfs2_sbd *sdp) mutex_unlock(&sdp->sd_rindex_mutex); } +static void gfs2_rindex_print(const struct gfs2_rgrpd *rgd) +{ + printk(KERN_INFO " ri_addr = %llu\n", (unsigned long long)rgd->rd_addr); + printk(KERN_INFO " ri_length = %u\n", rgd->rd_length); + printk(KERN_INFO " ri_data0 = %llu\n", (unsigned long long)rgd->rd_data0); + printk(KERN_INFO " ri_data = %u\n", rgd->rd_data); + printk(KERN_INFO " ri_bitbytes = %u\n", rgd->rd_bitbytes); +} + /** * gfs2_compute_bitstructs - Compute the bitmap sizes * @rgd: The resource group descriptor @@ -367,7 +380,7 @@ static int compute_bitstructs(struct gfs2_rgrpd *rgd) { struct gfs2_sbd *sdp = rgd->rd_sbd; struct gfs2_bitmap *bi; - u32 length = rgd->rd_ri.ri_length; /* # blocks in hdr & bitmap */ + u32 length = rgd->rd_length; /* # blocks in hdr & bitmap */ u32 bytes_left, bytes; int x; @@ -378,7 +391,7 @@ static int compute_bitstructs(struct gfs2_rgrpd *rgd) if (!rgd->rd_bits) return -ENOMEM; - bytes_left = rgd->rd_ri.ri_bitbytes; + bytes_left = rgd->rd_bitbytes; for (x = 0; x < length; x++) { bi = rgd->rd_bits + x; @@ -399,14 +412,14 @@ static int compute_bitstructs(struct gfs2_rgrpd *rgd) } else if (x + 1 == length) { bytes = bytes_left; bi->bi_offset = sizeof(struct gfs2_meta_header); - bi->bi_start = rgd->rd_ri.ri_bitbytes - bytes_left; + bi->bi_start = rgd->rd_bitbytes - bytes_left; bi->bi_len = bytes; /* other blocks */ } else { bytes = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header); bi->bi_offset = sizeof(struct gfs2_meta_header); - bi->bi_start = rgd->rd_ri.ri_bitbytes - bytes_left; + bi->bi_start = rgd->rd_bitbytes - bytes_left; bi->bi_len = bytes; } @@ -418,9 +431,9 @@ static int compute_bitstructs(struct gfs2_rgrpd *rgd) return -EIO; } bi = rgd->rd_bits + (length - 1); - if ((bi->bi_start + bi->bi_len) * GFS2_NBBY != rgd->rd_ri.ri_data) { + if ((bi->bi_start + bi->bi_len) * GFS2_NBBY != rgd->rd_data) { if (gfs2_consist_rgrpd(rgd)) { - gfs2_rindex_print(&rgd->rd_ri); + gfs2_rindex_print(rgd); fs_err(sdp, "start=%u len=%u offset=%u\n", bi->bi_start, bi->bi_len, bi->bi_offset); } @@ -431,9 +444,104 @@ static int compute_bitstructs(struct gfs2_rgrpd *rgd) } /** - * gfs2_ri_update - Pull in a new resource index from the disk + * gfs2_ri_total - Total up the file system space, according to the rindex. + * + */ +u64 gfs2_ri_total(struct gfs2_sbd *sdp) +{ + u64 total_data = 0; + struct inode *inode = sdp->sd_rindex; + struct gfs2_inode *ip = GFS2_I(inode); + char buf[sizeof(struct gfs2_rindex)]; + struct file_ra_state ra_state; + int error, rgrps; + + mutex_lock(&sdp->sd_rindex_mutex); + file_ra_state_init(&ra_state, inode->i_mapping); + for (rgrps = 0;; rgrps++) { + loff_t pos = rgrps * sizeof(struct gfs2_rindex); + + if (pos + sizeof(struct gfs2_rindex) >= ip->i_di.di_size) + break; + error = gfs2_internal_read(ip, &ra_state, buf, &pos, + sizeof(struct gfs2_rindex)); + if (error != sizeof(struct gfs2_rindex)) + break; + total_data += be32_to_cpu(((struct gfs2_rindex *)buf)->ri_data); + } + mutex_unlock(&sdp->sd_rindex_mutex); + return total_data; +} + +static void gfs2_rindex_in(struct gfs2_rgrpd *rgd, const void *buf) +{ + const struct gfs2_rindex *str = buf; + + rgd->rd_addr = be64_to_cpu(str->ri_addr); + rgd->rd_length = be32_to_cpu(str->ri_length); + rgd->rd_data0 = be64_to_cpu(str->ri_data0); + rgd->rd_data = be32_to_cpu(str->ri_data); + rgd->rd_bitbytes = be32_to_cpu(str->ri_bitbytes); +} + +/** + * read_rindex_entry - Pull in a new resource index entry from the disk * @gl: The glock covering the rindex inode * + * Returns: 0 on success, error code otherwise + */ + +static int read_rindex_entry(struct gfs2_inode *ip, + struct file_ra_state *ra_state) +{ + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + loff_t pos = sdp->sd_rgrps * sizeof(struct gfs2_rindex); + char buf[sizeof(struct gfs2_rindex)]; + int error; + struct gfs2_rgrpd *rgd; + + error = gfs2_internal_read(ip, ra_state, buf, &pos, + sizeof(struct gfs2_rindex)); + if (!error) + return 0; + if (error != sizeof(struct gfs2_rindex)) { + if (error > 0) + error = -EIO; + return error; + } + + rgd = kzalloc(sizeof(struct gfs2_rgrpd), GFP_NOFS); + error = -ENOMEM; + if (!rgd) + return error; + + mutex_init(&rgd->rd_mutex); + lops_init_le(&rgd->rd_le, &gfs2_rg_lops); + rgd->rd_sbd = sdp; + + list_add_tail(&rgd->rd_list, &sdp->sd_rindex_list); + list_add_tail(&rgd->rd_list_mru, &sdp->sd_rindex_mru_list); + + gfs2_rindex_in(rgd, buf); + error = compute_bitstructs(rgd); + if (error) + return error; + + error = gfs2_glock_get(sdp, rgd->rd_addr, + &gfs2_rgrp_glops, CREATE, &rgd->rd_gl); + if (error) + return error; + + rgd->rd_gl->gl_object = rgd; + rgd->rd_rg_vn = rgd->rd_gl->gl_vn - 1; + rgd->rd_flags |= GFS2_RDF_CHECK; + return error; +} + +/** + * gfs2_ri_update - Pull in a new resource index from the disk + * @ip: pointer to the rindex inode + * * Returns: 0 on successful update, error code otherwise */ @@ -441,13 +549,11 @@ static int gfs2_ri_update(struct gfs2_inode *ip) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct inode *inode = &ip->i_inode; - struct gfs2_rgrpd *rgd; - char buf[sizeof(struct gfs2_rindex)]; struct file_ra_state ra_state; - u64 junk = ip->i_di.di_size; + u64 rgrp_count = ip->i_di.di_size; int error; - if (do_div(junk, sizeof(struct gfs2_rindex))) { + if (do_div(rgrp_count, sizeof(struct gfs2_rindex))) { gfs2_consist_inode(ip); return -EIO; } @@ -455,50 +561,50 @@ static int gfs2_ri_update(struct gfs2_inode *ip) clear_rgrpdi(sdp); file_ra_state_init(&ra_state, inode->i_mapping); - for (sdp->sd_rgrps = 0;; sdp->sd_rgrps++) { - loff_t pos = sdp->sd_rgrps * sizeof(struct gfs2_rindex); - error = gfs2_internal_read(ip, &ra_state, buf, &pos, - sizeof(struct gfs2_rindex)); - if (!error) - break; - if (error != sizeof(struct gfs2_rindex)) { - if (error > 0) - error = -EIO; - goto fail; + for (sdp->sd_rgrps = 0; sdp->sd_rgrps < rgrp_count; sdp->sd_rgrps++) { + error = read_rindex_entry(ip, &ra_state); + if (error) { + clear_rgrpdi(sdp); + return error; } + } - rgd = kzalloc(sizeof(struct gfs2_rgrpd), GFP_NOFS); - error = -ENOMEM; - if (!rgd) - goto fail; - - mutex_init(&rgd->rd_mutex); - lops_init_le(&rgd->rd_le, &gfs2_rg_lops); - rgd->rd_sbd = sdp; - - list_add_tail(&rgd->rd_list, &sdp->sd_rindex_list); - list_add_tail(&rgd->rd_list_mru, &sdp->sd_rindex_mru_list); - - gfs2_rindex_in(&rgd->rd_ri, buf); - error = compute_bitstructs(rgd); - if (error) - goto fail; + sdp->sd_rindex_vn = ip->i_gl->gl_vn; + return 0; +} - error = gfs2_glock_get(sdp, rgd->rd_ri.ri_addr, - &gfs2_rgrp_glops, CREATE, &rgd->rd_gl); - if (error) - goto fail; +/** + * gfs2_ri_update_special - Pull in a new resource index from the disk + * + * This is a special version that's safe to call from gfs2_inplace_reserve_i. + * In this case we know that we don't have any resource groups in memory yet. + * + * @ip: pointer to the rindex inode + * + * Returns: 0 on successful update, error code otherwise + */ +static int gfs2_ri_update_special(struct gfs2_inode *ip) +{ + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + struct inode *inode = &ip->i_inode; + struct file_ra_state ra_state; + int error; - rgd->rd_gl->gl_object = rgd; - rgd->rd_rg_vn = rgd->rd_gl->gl_vn - 1; + file_ra_state_init(&ra_state, inode->i_mapping); + for (sdp->sd_rgrps = 0;; sdp->sd_rgrps++) { + /* Ignore partials */ + if ((sdp->sd_rgrps + 1) * sizeof(struct gfs2_rindex) > + ip->i_di.di_size) + break; + error = read_rindex_entry(ip, &ra_state); + if (error) { + clear_rgrpdi(sdp); + return error; + } } sdp->sd_rindex_vn = ip->i_gl->gl_vn; return 0; - -fail: - clear_rgrpdi(sdp); - return error; } /** @@ -543,6 +649,28 @@ int gfs2_rindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ri_gh) return error; } +static void gfs2_rgrp_in(struct gfs2_rgrp_host *rg, const void *buf) +{ + const struct gfs2_rgrp *str = buf; + + rg->rg_flags = be32_to_cpu(str->rg_flags); + rg->rg_free = be32_to_cpu(str->rg_free); + rg->rg_dinodes = be32_to_cpu(str->rg_dinodes); + rg->rg_igeneration = be64_to_cpu(str->rg_igeneration); +} + +static void gfs2_rgrp_out(const struct gfs2_rgrp_host *rg, void *buf) +{ + struct gfs2_rgrp *str = buf; + + str->rg_flags = cpu_to_be32(rg->rg_flags); + str->rg_free = cpu_to_be32(rg->rg_free); + str->rg_dinodes = cpu_to_be32(rg->rg_dinodes); + str->__pad = cpu_to_be32(0); + str->rg_igeneration = cpu_to_be64(rg->rg_igeneration); + memset(&str->rg_reserved, 0, sizeof(str->rg_reserved)); +} + /** * gfs2_rgrp_bh_get - Read in a RG's header and bitmaps * @rgd: the struct gfs2_rgrpd describing the RG to read in @@ -557,7 +685,7 @@ int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd) { struct gfs2_sbd *sdp = rgd->rd_sbd; struct gfs2_glock *gl = rgd->rd_gl; - unsigned int length = rgd->rd_ri.ri_length; + unsigned int length = rgd->rd_length; struct gfs2_bitmap *bi; unsigned int x, y; int error; @@ -575,7 +703,7 @@ int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd) for (x = 0; x < length; x++) { bi = rgd->rd_bits + x; - error = gfs2_meta_read(gl, rgd->rd_ri.ri_addr + x, 0, &bi->bi_bh); + error = gfs2_meta_read(gl, rgd->rd_addr + x, 0, &bi->bi_bh); if (error) goto fail; } @@ -637,7 +765,7 @@ void gfs2_rgrp_bh_hold(struct gfs2_rgrpd *rgd) void gfs2_rgrp_bh_put(struct gfs2_rgrpd *rgd) { struct gfs2_sbd *sdp = rgd->rd_sbd; - int x, length = rgd->rd_ri.ri_length; + int x, length = rgd->rd_length; spin_lock(&sdp->sd_rindex_spin); gfs2_assert_warn(rgd->rd_sbd, rgd->rd_bh_count); @@ -660,7 +788,7 @@ void gfs2_rgrp_bh_put(struct gfs2_rgrpd *rgd) void gfs2_rgrp_repolish_clones(struct gfs2_rgrpd *rgd) { struct gfs2_sbd *sdp = rgd->rd_sbd; - unsigned int length = rgd->rd_ri.ri_length; + unsigned int length = rgd->rd_length; unsigned int x; for (x = 0; x < length; x++) { @@ -722,6 +850,38 @@ static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_alloc *al) } /** + * try_rgrp_unlink - Look for any unlinked, allocated, but unused inodes + * @rgd: The rgrp + * + * Returns: The inode, if one has been found + */ + +static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked) +{ + struct inode *inode; + u32 goal = 0; + u64 no_addr; + + for(;;) { + goal = rgblk_search(rgd, goal, GFS2_BLKST_UNLINKED, + GFS2_BLKST_UNLINKED); + if (goal == 0) + return 0; + no_addr = goal + rgd->rd_data0; + if (no_addr <= *last_unlinked) + continue; + *last_unlinked = no_addr; + inode = gfs2_inode_lookup(rgd->rd_sbd->sd_vfs, DT_UNKNOWN, + no_addr, -1); + if (!IS_ERR(inode)) + return inode; + } + + rgd->rd_flags &= ~GFS2_RDF_CHECK; + return NULL; +} + +/** * recent_rgrp_first - get first RG from "recent" list * @sdp: The GFS2 superblock * @rglast: address of the rgrp used last @@ -743,7 +903,7 @@ static struct gfs2_rgrpd *recent_rgrp_first(struct gfs2_sbd *sdp, goto first; list_for_each_entry(rgd, &sdp->sd_rindex_recent_list, rd_recent) { - if (rgd->rd_ri.ri_addr == rglast) + if (rgd->rd_addr == rglast) goto out; } @@ -882,8 +1042,9 @@ static void forward_rgrp_set(struct gfs2_sbd *sdp, struct gfs2_rgrpd *rgd) * Returns: errno */ -static int get_local_rgrp(struct gfs2_inode *ip) +static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) { + struct inode *inode = NULL; struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_rgrpd *rgd, *begin = NULL; struct gfs2_alloc *al = &ip->i_alloc; @@ -903,7 +1064,11 @@ static int get_local_rgrp(struct gfs2_inode *ip) case 0: if (try_rgrp_fit(rgd, al)) goto out; + if (rgd->rd_flags & GFS2_RDF_CHECK) + inode = try_rgrp_unlink(rgd, last_unlinked); gfs2_glock_dq_uninit(&al->al_rgd_gh); + if (inode) + return inode; rgd = recent_rgrp_next(rgd, 1); break; @@ -912,7 +1077,7 @@ static int get_local_rgrp(struct gfs2_inode *ip) break; default: - return error; + return ERR_PTR(error); } } @@ -927,7 +1092,11 @@ static int get_local_rgrp(struct gfs2_inode *ip) case 0: if (try_rgrp_fit(rgd, al)) goto out; + if (rgd->rd_flags & GFS2_RDF_CHECK) + inode = try_rgrp_unlink(rgd, last_unlinked); gfs2_glock_dq_uninit(&al->al_rgd_gh); + if (inode) + return inode; break; case GLR_TRYFAILED: @@ -935,7 +1104,7 @@ static int get_local_rgrp(struct gfs2_inode *ip) break; default: - return error; + return ERR_PTR(error); } rgd = gfs2_rgrpd_get_next(rgd); @@ -944,7 +1113,7 @@ static int get_local_rgrp(struct gfs2_inode *ip) if (rgd == begin) { if (++loops >= 3) - return -ENOSPC; + return ERR_PTR(-ENOSPC); if (!skipped) loops++; flags = 0; @@ -954,7 +1123,7 @@ static int get_local_rgrp(struct gfs2_inode *ip) } out: - ip->i_last_rg_alloc = rgd->rd_ri.ri_addr; + ip->i_last_rg_alloc = rgd->rd_addr; if (begin) { recent_rgrp_add(rgd); @@ -964,7 +1133,7 @@ out: forward_rgrp_set(sdp, rgd); } - return 0; + return NULL; } /** @@ -978,19 +1147,33 @@ int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, unsigned int line) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_alloc *al = &ip->i_alloc; - int error; + struct inode *inode; + int error = 0; + u64 last_unlinked = 0; if (gfs2_assert_warn(sdp, al->al_requested)) return -EINVAL; - error = gfs2_rindex_hold(sdp, &al->al_ri_gh); +try_again: + /* We need to hold the rindex unless the inode we're using is + the rindex itself, in which case it's already held. */ + if (ip != GFS2_I(sdp->sd_rindex)) + error = gfs2_rindex_hold(sdp, &al->al_ri_gh); + else if (!sdp->sd_rgrps) /* We may not have the rindex read in, so: */ + error = gfs2_ri_update_special(ip); + if (error) return error; - error = get_local_rgrp(ip); - if (error) { - gfs2_glock_dq_uninit(&al->al_ri_gh); - return error; + inode = get_local_rgrp(ip, &last_unlinked); + if (inode) { + if (ip != GFS2_I(sdp->sd_rindex)) + gfs2_glock_dq_uninit(&al->al_ri_gh); + if (IS_ERR(inode)) + return PTR_ERR(inode); + iput(inode); + gfs2_log_flush(sdp, NULL); + goto try_again; } al->al_file = file; @@ -1019,7 +1202,8 @@ void gfs2_inplace_release(struct gfs2_inode *ip) al->al_rgd = NULL; gfs2_glock_dq_uninit(&al->al_rgd_gh); - gfs2_glock_dq_uninit(&al->al_ri_gh); + if (ip != GFS2_I(sdp->sd_rindex)) + gfs2_glock_dq_uninit(&al->al_ri_gh); } /** @@ -1037,8 +1221,8 @@ unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block) unsigned int buf; unsigned char type; - length = rgd->rd_ri.ri_length; - rgrp_block = block - rgd->rd_ri.ri_data0; + length = rgd->rd_length; + rgrp_block = block - rgd->rd_data0; for (buf = 0; buf < length; buf++) { bi = rgd->rd_bits + buf; @@ -1077,10 +1261,10 @@ unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block) */ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, - unsigned char old_state, unsigned char new_state) + unsigned char old_state, unsigned char new_state) { struct gfs2_bitmap *bi = NULL; - u32 length = rgd->rd_ri.ri_length; + u32 length = rgd->rd_length; u32 blk = 0; unsigned int buf, x; @@ -1118,17 +1302,18 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, goal = 0; } - if (gfs2_assert_withdraw(rgd->rd_sbd, x <= length)) - blk = 0; + if (old_state != new_state) { + gfs2_assert_withdraw(rgd->rd_sbd, blk != BFITNOENT); - gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1); - gfs2_setbit(rgd, bi->bi_bh->b_data + bi->bi_offset, - bi->bi_len, blk, new_state); - if (bi->bi_clone) - gfs2_setbit(rgd, bi->bi_clone + bi->bi_offset, + gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1); + gfs2_setbit(rgd, bi->bi_bh->b_data + bi->bi_offset, bi->bi_len, blk, new_state); + if (bi->bi_clone) + gfs2_setbit(rgd, bi->bi_clone + bi->bi_offset, + bi->bi_len, blk, new_state); + } - return bi->bi_start * GFS2_NBBY + blk; + return (blk == BFITNOENT) ? 0 : (bi->bi_start * GFS2_NBBY) + blk; } /** @@ -1156,9 +1341,9 @@ static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart, return NULL; } - length = rgd->rd_ri.ri_length; + length = rgd->rd_length; - rgrp_blk = bstart - rgd->rd_ri.ri_data0; + rgrp_blk = bstart - rgd->rd_data0; while (blen--) { for (buf = 0; buf < length; buf++) { @@ -1202,15 +1387,15 @@ u64 gfs2_alloc_data(struct gfs2_inode *ip) u32 goal, blk; u64 block; - if (rgrp_contains_block(&rgd->rd_ri, ip->i_di.di_goal_data)) - goal = ip->i_di.di_goal_data - rgd->rd_ri.ri_data0; + if (rgrp_contains_block(rgd, ip->i_di.di_goal_data)) + goal = ip->i_di.di_goal_data - rgd->rd_data0; else goal = rgd->rd_last_alloc_data; blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, GFS2_BLKST_USED); rgd->rd_last_alloc_data = blk; - block = rgd->rd_ri.ri_data0 + blk; + block = rgd->rd_data0 + blk; ip->i_di.di_goal_data = block; gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free); @@ -1246,15 +1431,15 @@ u64 gfs2_alloc_meta(struct gfs2_inode *ip) u32 goal, blk; u64 block; - if (rgrp_contains_block(&rgd->rd_ri, ip->i_di.di_goal_meta)) - goal = ip->i_di.di_goal_meta - rgd->rd_ri.ri_data0; + if (rgrp_contains_block(rgd, ip->i_di.di_goal_meta)) + goal = ip->i_di.di_goal_meta - rgd->rd_data0; else goal = rgd->rd_last_alloc_meta; blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, GFS2_BLKST_USED); rgd->rd_last_alloc_meta = blk; - block = rgd->rd_ri.ri_data0 + blk; + block = rgd->rd_data0 + blk; ip->i_di.di_goal_meta = block; gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free); @@ -1296,7 +1481,7 @@ u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation) rgd->rd_last_alloc_meta = blk; - block = rgd->rd_ri.ri_data0 + blk; + block = rgd->rd_data0 + blk; gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free); rgd->rd_rg.rg_free--; @@ -1379,7 +1564,7 @@ void gfs2_unlink_di(struct inode *inode) struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); struct gfs2_rgrpd *rgd; - u64 blkno = ip->i_num.no_addr; + u64 blkno = ip->i_no_addr; rgd = rgblk_free(sdp, blkno, 1, GFS2_BLKST_UNLINKED); if (!rgd) @@ -1414,9 +1599,9 @@ static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno) void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip) { - gfs2_free_uninit_di(rgd, ip->i_num.no_addr); + gfs2_free_uninit_di(rgd, ip->i_no_addr); gfs2_quota_change(ip, -1, ip->i_inode.i_uid, ip->i_inode.i_gid); - gfs2_meta_wipe(ip, ip->i_num.no_addr, 1); + gfs2_meta_wipe(ip, ip->i_no_addr, 1); } /** diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h index b01e0cfc99b..b4c6adfc6f2 100644 --- a/fs/gfs2/rgrp.h +++ b/fs/gfs2/rgrp.h @@ -65,5 +65,6 @@ void gfs2_rlist_add(struct gfs2_sbd *sdp, struct gfs2_rgrp_list *rlist, void gfs2_rlist_alloc(struct gfs2_rgrp_list *rlist, unsigned int state, int flags); void gfs2_rlist_free(struct gfs2_rgrp_list *rlist); +u64 gfs2_ri_total(struct gfs2_sbd *sdp); #endif /* __RGRP_DOT_H__ */ diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 4fdda974dc8..f916b9740c7 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -95,8 +95,8 @@ int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb_host *sb, int silent) { unsigned int x; - if (sb->sb_header.mh_magic != GFS2_MAGIC || - sb->sb_header.mh_type != GFS2_METATYPE_SB) { + if (sb->sb_magic != GFS2_MAGIC || + sb->sb_type != GFS2_METATYPE_SB) { if (!silent) printk(KERN_WARNING "GFS2: not a GFS2 filesystem\n"); return -EINVAL; @@ -174,10 +174,31 @@ static int end_bio_io_page(struct bio *bio, unsigned int bytes_done, int error) return 0; } +static void gfs2_sb_in(struct gfs2_sb_host *sb, const void *buf) +{ + const struct gfs2_sb *str = buf; + + sb->sb_magic = be32_to_cpu(str->sb_header.mh_magic); + sb->sb_type = be32_to_cpu(str->sb_header.mh_type); + sb->sb_format = be32_to_cpu(str->sb_header.mh_format); + sb->sb_fs_format = be32_to_cpu(str->sb_fs_format); + sb->sb_multihost_format = be32_to_cpu(str->sb_multihost_format); + sb->sb_bsize = be32_to_cpu(str->sb_bsize); + sb->sb_bsize_shift = be32_to_cpu(str->sb_bsize_shift); + sb->sb_master_dir.no_addr = be64_to_cpu(str->sb_master_dir.no_addr); + sb->sb_master_dir.no_formal_ino = be64_to_cpu(str->sb_master_dir.no_formal_ino); + sb->sb_root_dir.no_addr = be64_to_cpu(str->sb_root_dir.no_addr); + sb->sb_root_dir.no_formal_ino = be64_to_cpu(str->sb_root_dir.no_formal_ino); + + memcpy(sb->sb_lockproto, str->sb_lockproto, GFS2_LOCKNAME_LEN); + memcpy(sb->sb_locktable, str->sb_locktable, GFS2_LOCKNAME_LEN); +} + /** * gfs2_read_super - Read the gfs2 super block from disk - * @sb: The VFS super block + * @sdp: The GFS2 super block * @sector: The location of the super block + * @error: The error code to return * * This uses the bio functions to read the super block from disk * because we want to be 100% sure that we never read cached data. @@ -189,17 +210,19 @@ static int end_bio_io_page(struct bio *bio, unsigned int bytes_done, int error) * the master directory (contains pointers to journals etc) and the * root directory. * - * Returns: A page containing the sb or NULL + * Returns: 0 on success or error */ -struct page *gfs2_read_super(struct super_block *sb, sector_t sector) +int gfs2_read_super(struct gfs2_sbd *sdp, sector_t sector) { + struct super_block *sb = sdp->sd_vfs; + struct gfs2_sb *p; struct page *page; struct bio *bio; page = alloc_page(GFP_KERNEL); if (unlikely(!page)) - return NULL; + return -ENOBUFS; ClearPageUptodate(page); ClearPageDirty(page); @@ -208,7 +231,7 @@ struct page *gfs2_read_super(struct super_block *sb, sector_t sector) bio = bio_alloc(GFP_KERNEL, 1); if (unlikely(!bio)) { __free_page(page); - return NULL; + return -ENOBUFS; } bio->bi_sector = sector * (sb->s_blocksize >> 9); @@ -222,9 +245,13 @@ struct page *gfs2_read_super(struct super_block *sb, sector_t sector) bio_put(bio); if (!PageUptodate(page)) { __free_page(page); - return NULL; + return -EIO; } - return page; + p = kmap(page); + gfs2_sb_in(&sdp->sd_sb, p); + kunmap(page); + __free_page(page); + return 0; } /** @@ -241,19 +268,13 @@ int gfs2_read_sb(struct gfs2_sbd *sdp, struct gfs2_glock *gl, int silent) u32 tmp_blocks; unsigned int x; int error; - struct page *page; - char *sb; - page = gfs2_read_super(sdp->sd_vfs, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift); - if (!page) { + error = gfs2_read_super(sdp, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift); + if (error) { if (!silent) fs_err(sdp, "can't read superblock\n"); - return -EIO; + return error; } - sb = kmap(page); - gfs2_sb_in(&sdp->sd_sb, sb); - kunmap(page); - __free_page(page); error = gfs2_check_sb(sdp, &sdp->sd_sb, silent); if (error) @@ -360,7 +381,7 @@ int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh) name.len = sprintf(buf, "journal%u", sdp->sd_journals); name.hash = gfs2_disk_hash(name.name, name.len); - error = gfs2_dir_search(sdp->sd_jindex, &name, NULL, NULL); + error = gfs2_dir_check(sdp->sd_jindex, &name, NULL); if (error == -ENOENT) { error = 0; break; @@ -593,6 +614,24 @@ int gfs2_make_fs_ro(struct gfs2_sbd *sdp) return error; } +static void gfs2_statfs_change_in(struct gfs2_statfs_change_host *sc, const void *buf) +{ + const struct gfs2_statfs_change *str = buf; + + sc->sc_total = be64_to_cpu(str->sc_total); + sc->sc_free = be64_to_cpu(str->sc_free); + sc->sc_dinodes = be64_to_cpu(str->sc_dinodes); +} + +static void gfs2_statfs_change_out(const struct gfs2_statfs_change_host *sc, void *buf) +{ + struct gfs2_statfs_change *str = buf; + + str->sc_total = cpu_to_be64(sc->sc_total); + str->sc_free = cpu_to_be64(sc->sc_free); + str->sc_dinodes = cpu_to_be64(sc->sc_dinodes); +} + int gfs2_statfs_init(struct gfs2_sbd *sdp) { struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); @@ -772,7 +811,7 @@ static int statfs_slow_fill(struct gfs2_rgrpd *rgd, struct gfs2_statfs_change_host *sc) { gfs2_rgrp_verify(rgd); - sc->sc_total += rgd->rd_ri.ri_data; + sc->sc_total += rgd->rd_data; sc->sc_free += rgd->rd_rg.rg_free; sc->sc_dinodes += rgd->rd_rg.rg_dinodes; return 0; diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h index e590b2df11d..60a870e430b 100644 --- a/fs/gfs2/super.h +++ b/fs/gfs2/super.h @@ -16,7 +16,7 @@ void gfs2_tune_init(struct gfs2_tune *gt); int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb_host *sb, int silent); int gfs2_read_sb(struct gfs2_sbd *sdp, struct gfs2_glock *gl, int silent); -struct page *gfs2_read_super(struct super_block *sb, sector_t sector); +int gfs2_read_super(struct gfs2_sbd *sdp, sector_t sector); static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp) { diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index 601eaa1b9ed..424a0774eda 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -115,8 +115,8 @@ int gfs2_consist_inode_i(struct gfs2_inode *ip, int cluster_wide, "GFS2: fsid=%s: inode = %llu %llu\n" "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", sdp->sd_fsname, - sdp->sd_fsname, (unsigned long long)ip->i_num.no_formal_ino, - (unsigned long long)ip->i_num.no_addr, + sdp->sd_fsname, (unsigned long long)ip->i_no_formal_ino, + (unsigned long long)ip->i_no_addr, sdp->sd_fsname, function, file, line); return rv; } @@ -137,7 +137,7 @@ int gfs2_consist_rgrpd_i(struct gfs2_rgrpd *rgd, int cluster_wide, "GFS2: fsid=%s: RG = %llu\n" "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", sdp->sd_fsname, - sdp->sd_fsname, (unsigned long long)rgd->rd_ri.ri_addr, + sdp->sd_fsname, (unsigned long long)rgd->rd_addr, sdp->sd_fsname, function, file, line); return rv; } diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 9a934db0bd8..bc835f272a6 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -607,7 +607,7 @@ static const struct file_operations hfs_file_operations = { .write = do_sync_write, .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, .fsync = file_fsync, .open = hfs_file_open, .release = hfs_file_release, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 45dab5d6cc1..409ce5429c9 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -288,7 +288,7 @@ static const struct file_operations hfsplus_file_operations = { .write = do_sync_write, .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, .fsync = file_fsync, .open = hfsplus_file_open, .release = hfsplus_file_release, diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 8286491dbf3..c77862032e8 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -390,7 +390,7 @@ int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync) static const struct file_operations hostfs_file_fops = { .llseek = generic_file_llseek, .read = do_sync_read, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, .aio_read = generic_file_aio_read, .aio_write = generic_file_aio_write, .write = do_sync_write, diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index b4eafc0f1e5..5b53e5c5d8d 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -129,7 +129,7 @@ const struct file_operations hpfs_file_ops = .mmap = generic_file_mmap, .release = hpfs_file_release, .fsync = hpfs_file_fsync, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; const struct inode_operations hpfs_file_iops = diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 99871279a1e..c2530197be0 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -47,7 +47,7 @@ const struct file_operations jffs2_file_operations = .ioctl = jffs2_ioctl, .mmap = generic_file_readonly_mmap, .fsync = jffs2_fsync, - .sendfile = generic_file_sendfile + .splice_read = generic_file_splice_read, }; /* jffs2_file_inode_operations */ diff --git a/fs/jfs/file.c b/fs/jfs/file.c index f7f8eff19b7..87eb93694af 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -108,7 +108,6 @@ const struct file_operations jfs_file_operations = { .aio_read = generic_file_aio_read, .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, - .sendfile = generic_file_sendfile, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, .fsync = jfs_fsync, diff --git a/fs/minix/file.c b/fs/minix/file.c index f92baa1d757..17765f697e5 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -23,7 +23,7 @@ const struct file_operations minix_file_operations = { .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, .fsync = minix_sync_file, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; const struct inode_operations minix_file_inode_operations = { diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 9eb8eb4e4a0..8689b736fdd 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -41,7 +41,9 @@ static int nfs_file_open(struct inode *, struct file *); static int nfs_file_release(struct inode *, struct file *); static loff_t nfs_file_llseek(struct file *file, loff_t offset, int origin); static int nfs_file_mmap(struct file *, struct vm_area_struct *); -static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *); +static ssize_t nfs_file_splice_read(struct file *filp, loff_t *ppos, + struct pipe_inode_info *pipe, + size_t count, unsigned int flags); static ssize_t nfs_file_read(struct kiocb *, const struct iovec *iov, unsigned long nr_segs, loff_t pos); static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov, @@ -65,7 +67,7 @@ const struct file_operations nfs_file_operations = { .fsync = nfs_fsync, .lock = nfs_lock, .flock = nfs_flock, - .sendfile = nfs_file_sendfile, + .splice_read = nfs_file_splice_read, .check_flags = nfs_check_flags, }; @@ -224,20 +226,21 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, } static ssize_t -nfs_file_sendfile(struct file *filp, loff_t *ppos, size_t count, - read_actor_t actor, void *target) +nfs_file_splice_read(struct file *filp, loff_t *ppos, + struct pipe_inode_info *pipe, size_t count, + unsigned int flags) { struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; ssize_t res; - dfprintk(VFS, "nfs: sendfile(%s/%s, %lu@%Lu)\n", + dfprintk(VFS, "nfs: splice_read(%s/%s, %lu@%Lu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (unsigned long long) *ppos); res = nfs_revalidate_mapping(inode, filp->f_mapping); if (!res) - res = generic_file_sendfile(filp, ppos, count, actor, target); + res = generic_file_splice_read(filp, ppos, pipe, count, flags); return res; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 7e6aa245b5d..8604e35bd48 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -23,7 +23,7 @@ #include <linux/file.h> #include <linux/mount.h> #include <linux/major.h> -#include <linux/ext2_fs.h> +#include <linux/splice.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/fcntl.h> @@ -801,26 +801,32 @@ found: } /* - * Grab and keep cached pages assosiated with a file in the svc_rqst - * so that they can be passed to the netowork sendmsg/sendpage routines - * directrly. They will be released after the sending has completed. + * Grab and keep cached pages associated with a file in the svc_rqst + * so that they can be passed to the network sendmsg/sendpage routines + * directly. They will be released after the sending has completed. */ static int -nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset , unsigned long size) +nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, + struct splice_desc *sd) { - unsigned long count = desc->count; - struct svc_rqst *rqstp = desc->arg.data; + struct svc_rqst *rqstp = sd->u.data; struct page **pp = rqstp->rq_respages + rqstp->rq_resused; + struct page *page = buf->page; + size_t size; + int ret; + + ret = buf->ops->confirm(pipe, buf); + if (unlikely(ret)) + return ret; - if (size > count) - size = count; + size = sd->len; if (rqstp->rq_res.page_len == 0) { get_page(page); put_page(*pp); *pp = page; rqstp->rq_resused++; - rqstp->rq_res.page_base = offset; + rqstp->rq_res.page_base = buf->offset; rqstp->rq_res.page_len = size; } else if (page != pp[-1]) { get_page(page); @@ -832,11 +838,15 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset } else rqstp->rq_res.page_len += size; - desc->count = count - size; - desc->written += size; return size; } +static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe, + struct splice_desc *sd) +{ + return __splice_from_pipe(pipe, sd, nfsd_splice_actor); +} + static __be32 nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, loff_t offset, struct kvec *vec, int vlen, unsigned long *count) @@ -861,10 +871,15 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, if (ra && ra->p_set) file->f_ra = ra->p_ra; - if (file->f_op->sendfile && rqstp->rq_sendfile_ok) { - rqstp->rq_resused = 1; - host_err = file->f_op->sendfile(file, &offset, *count, - nfsd_read_actor, rqstp); + if (file->f_op->splice_read && rqstp->rq_splice_ok) { + struct splice_desc sd = { + .len = 0, + .total_len = *count, + .pos = offset, + .u.data = rqstp, + }; + + host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); } else { oldfs = get_fs(); set_fs(KERNEL_DS); diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 7ed56390b58..ffcc504a166 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -2276,7 +2276,7 @@ const struct file_operations ntfs_file_ops = { mounted filesystem. */ .mmap = generic_file_mmap, /* Mmap file. */ .open = ntfs_file_open, /* Open file. */ - .sendfile = generic_file_sendfile, /* Zero-copy data send with + .splice_read = generic_file_splice_read /* Zero-copy data send with the data source being on the ntfs partition. We do not need to care about the diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index ac6c96431bb..4979b667571 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -31,7 +31,7 @@ #include <linux/pagemap.h> #include <linux/uio.h> #include <linux/sched.h> -#include <linux/pipe_fs_i.h> +#include <linux/splice.h> #include <linux/mount.h> #include <linux/writeback.h> @@ -1583,7 +1583,7 @@ static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe, ssize_t copied = 0; struct ocfs2_splice_write_priv sp; - ret = buf->ops->pin(pipe, buf); + ret = buf->ops->confirm(pipe, buf); if (ret) goto out; @@ -1604,7 +1604,7 @@ static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe, * might enter ocfs2_buffered_write_cluster() more * than once, so keep track of our progress here. */ - copied = ocfs2_buffered_write_cluster(sd->file, + copied = ocfs2_buffered_write_cluster(sd->u.file, (loff_t)sd->pos + total, count, ocfs2_map_and_write_splice_data, @@ -1636,9 +1636,14 @@ static ssize_t __ocfs2_file_splice_write(struct pipe_inode_info *pipe, int ret, err; struct address_space *mapping = out->f_mapping; struct inode *inode = mapping->host; - - ret = __splice_from_pipe(pipe, out, ppos, len, flags, - ocfs2_splice_write_actor); + struct splice_desc sd = { + .total_len = len, + .flags = flags, + .pos = *ppos, + .u.file = out, + }; + + ret = __splice_from_pipe(pipe, &sd, ocfs2_splice_write_actor); if (ret > 0) { *ppos += ret; @@ -1817,7 +1822,6 @@ const struct inode_operations ocfs2_special_file_iops = { const struct file_operations ocfs2_fops = { .read = do_sync_read, .write = do_sync_write, - .sendfile = generic_file_sendfile, .mmap = ocfs2_mmap, .fsync = ocfs2_sync_file, .release = ocfs2_file_release, diff --git a/fs/partitions/ibm.c b/fs/partitions/ibm.c index 9f7ad4244f6..1e064c4a4f8 100644 --- a/fs/partitions/ibm.c +++ b/fs/partitions/ibm.c @@ -45,7 +45,7 @@ ibm_partition(struct parsed_partitions *state, struct block_device *bdev) { int blocksize, offset, size,res; loff_t i_size; - dasd_information_t *info; + dasd_information2_t *info; struct hd_geometry *geo; char type[5] = {0,}; char name[7] = {0,}; @@ -64,14 +64,17 @@ ibm_partition(struct parsed_partitions *state, struct block_device *bdev) if (i_size == 0) goto out_exit; - if ((info = kmalloc(sizeof(dasd_information_t), GFP_KERNEL)) == NULL) + info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL); + if (info == NULL) goto out_exit; - if ((geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL)) == NULL) + geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL); + if (geo == NULL) goto out_nogeo; - if ((label = kmalloc(sizeof(union label_t), GFP_KERNEL)) == NULL) + label = kmalloc(sizeof(union label_t), GFP_KERNEL); + if (label == NULL) goto out_nolab; - if (ioctl_by_bdev(bdev, BIODASDINFO, (unsigned long)info) != 0 || + if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0 || ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0) goto out_freeall; @@ -96,84 +99,108 @@ ibm_partition(struct parsed_partitions *state, struct block_device *bdev) res = 1; /* - * Three different types: CMS1, VOL1 and LNX1/unlabeled + * Three different formats: LDL, CDL and unformated disk + * + * identified by info->format + * + * unformated disks we do not have to care about */ - if (strncmp(type, "CMS1", 4) == 0) { - /* - * VM style CMS1 labeled disk - */ - if (label->cms.disk_offset != 0) { - printk("CMS1/%8s(MDSK):", name); - /* disk is reserved minidisk */ - blocksize = label->cms.block_size; - offset = label->cms.disk_offset; - size = (label->cms.block_count - 1) * (blocksize >> 9); + if (info->format == DASD_FORMAT_LDL) { + if (strncmp(type, "CMS1", 4) == 0) { + /* + * VM style CMS1 labeled disk + */ + if (label->cms.disk_offset != 0) { + printk("CMS1/%8s(MDSK):", name); + /* disk is reserved minidisk */ + blocksize = label->cms.block_size; + offset = label->cms.disk_offset; + size = (label->cms.block_count - 1) + * (blocksize >> 9); + } else { + printk("CMS1/%8s:", name); + offset = (info->label_block + 1); + size = i_size >> 9; + } } else { - printk("CMS1/%8s:", name); + /* + * Old style LNX1 or unlabeled disk + */ + if (strncmp(type, "LNX1", 4) == 0) + printk ("LNX1/%8s:", name); + else + printk("(nonl)"); offset = (info->label_block + 1); size = i_size >> 9; } put_partition(state, 1, offset*(blocksize >> 9), - size-offset*(blocksize >> 9)); - } else if ((strncmp(type, "VOL1", 4) == 0) && - (!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) { + size-offset*(blocksize >> 9)); + } else if (info->format == DASD_FORMAT_CDL) { /* - * New style VOL1 labeled disk + * New style CDL formatted disk */ unsigned int blk; int counter; - printk("VOL1/%8s:", name); - - /* get block number and read then go through format1 labels */ - blk = cchhb2blk(&label->vol.vtoc, geo) + 1; - counter = 0; - while ((data = read_dev_sector(bdev, blk*(blocksize/512), - §)) != NULL) { - struct vtoc_format1_label f1; - - memcpy(&f1, data, sizeof(struct vtoc_format1_label)); - put_dev_sector(sect); - - /* skip FMT4 / FMT5 / FMT7 labels */ - if (f1.DS1FMTID == _ascebc['4'] - || f1.DS1FMTID == _ascebc['5'] - || f1.DS1FMTID == _ascebc['7']) { - blk++; - continue; - } - - /* only FMT1 valid at this point */ - if (f1.DS1FMTID != _ascebc['1']) - break; - - /* OK, we got valid partition data */ - offset = cchh2blk(&f1.DS1EXT1.llimit, geo); - size = cchh2blk(&f1.DS1EXT1.ulimit, geo) - - offset + geo->sectors; - if (counter >= state->limit) - break; - put_partition(state, counter + 1, - offset * (blocksize >> 9), - size * (blocksize >> 9)); - counter++; - blk++; - } - if (!data) - /* Are we not supposed to report this ? */ - goto out_readerr; - } else { /* - * Old style LNX1 or unlabeled disk + * check if VOL1 label is available + * if not, something is wrong, skipping partition detection */ - if (strncmp(type, "LNX1", 4) == 0) - printk ("LNX1/%8s:", name); - else - printk("(nonl)/%8s:", name); - offset = (info->label_block + 1); - size = i_size >> 9; - put_partition(state, 1, offset*(blocksize >> 9), - size-offset*(blocksize >> 9)); + if (strncmp(type, "VOL1", 4) == 0) { + printk("VOL1/%8s:", name); + /* + * get block number and read then go through format1 + * labels + */ + blk = cchhb2blk(&label->vol.vtoc, geo) + 1; + counter = 0; + data = read_dev_sector(bdev, blk * (blocksize/512), + §); + while (data != NULL) { + struct vtoc_format1_label f1; + + memcpy(&f1, data, + sizeof(struct vtoc_format1_label)); + put_dev_sector(sect); + + /* skip FMT4 / FMT5 / FMT7 labels */ + if (f1.DS1FMTID == _ascebc['4'] + || f1.DS1FMTID == _ascebc['5'] + || f1.DS1FMTID == _ascebc['7']) { + blk++; + data = read_dev_sector(bdev, blk * + (blocksize/512), + §); + continue; + } + + /* only FMT1 valid at this point */ + if (f1.DS1FMTID != _ascebc['1']) + break; + + /* OK, we got valid partition data */ + offset = cchh2blk(&f1.DS1EXT1.llimit, geo); + size = cchh2blk(&f1.DS1EXT1.ulimit, geo) - + offset + geo->sectors; + if (counter >= state->limit) + break; + put_partition(state, counter + 1, + offset * (blocksize >> 9), + size * (blocksize >> 9)); + counter++; + blk++; + data = read_dev_sector(bdev, + blk * (blocksize/512), + §); + } + + if (!data) + /* Are we not supposed to report this ? */ + goto out_readerr; + } else + printk(KERN_WARNING "Warning, expected Label VOL1 not " + "found, treating as CDL formated Disk"); + } printk("\n"); diff --git a/fs/pipe.c b/fs/pipe.c index 3a89592bdf5..d007830d9c8 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -164,6 +164,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe, page_cache_release(page); } +/** + * generic_pipe_buf_map - virtually map a pipe buffer + * @pipe: the pipe that the buffer belongs to + * @buf: the buffer that should be mapped + * @atomic: whether to use an atomic map + * + * Description: + * This function returns a kernel virtual address mapping for the + * passed in @pipe_buffer. If @atomic is set, an atomic map is provided + * and the caller has to be careful not to fault before calling + * the unmap function. + * + * Note that this function occupies KM_USER0 if @atomic != 0. + */ void *generic_pipe_buf_map(struct pipe_inode_info *pipe, struct pipe_buffer *buf, int atomic) { @@ -175,6 +189,15 @@ void *generic_pipe_buf_map(struct pipe_inode_info *pipe, return kmap(buf->page); } +/** + * generic_pipe_buf_unmap - unmap a previously mapped pipe buffer + * @pipe: the pipe that the buffer belongs to + * @buf: the buffer that should be unmapped + * @map_data: the data that the mapping function returned + * + * Description: + * This function undoes the mapping that ->map() provided. + */ void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, struct pipe_buffer *buf, void *map_data) { @@ -185,11 +208,28 @@ void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, kunmap(buf->page); } +/** + * generic_pipe_buf_steal - attempt to take ownership of a @pipe_buffer + * @pipe: the pipe that the buffer belongs to + * @buf: the buffer to attempt to steal + * + * Description: + * This function attempts to steal the @struct page attached to + * @buf. If successful, this function returns 0 and returns with + * the page locked. The caller may then reuse the page for whatever + * he wishes, the typical use is insertion into a different file + * page cache. + */ int generic_pipe_buf_steal(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { struct page *page = buf->page; + /* + * A reference of one is golden, that means that the owner of this + * page is the only one holding a reference to it. lock the page + * and return OK. + */ if (page_count(page) == 1) { lock_page(page); return 0; @@ -198,12 +238,32 @@ int generic_pipe_buf_steal(struct pipe_inode_info *pipe, return 1; } -void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf) +/** + * generic_pipe_buf_get - get a reference to a @struct pipe_buffer + * @pipe: the pipe that the buffer belongs to + * @buf: the buffer to get a reference to + * + * Description: + * This function grabs an extra reference to @buf. It's used in + * in the tee() system call, when we duplicate the buffers in one + * pipe into another. + */ +void generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { page_cache_get(buf->page); } -int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf) +/** + * generic_pipe_buf_confirm - verify contents of the pipe buffer + * @pipe: the pipe that the buffer belongs to + * @buf: the buffer to confirm + * + * Description: + * This function does nothing, because the generic pipe code uses + * pages that are always good when inserted into the pipe. + */ +int generic_pipe_buf_confirm(struct pipe_inode_info *info, + struct pipe_buffer *buf) { return 0; } @@ -212,7 +272,7 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = { .can_merge = 1, .map = generic_pipe_buf_map, .unmap = generic_pipe_buf_unmap, - .pin = generic_pipe_buf_pin, + .confirm = generic_pipe_buf_confirm, .release = anon_pipe_buf_release, .steal = generic_pipe_buf_steal, .get = generic_pipe_buf_get, @@ -252,7 +312,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov, if (chars > total_len) chars = total_len; - error = ops->pin(pipe, buf); + error = ops->confirm(pipe, buf); if (error) { if (!ret) error = ret; @@ -373,7 +433,7 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov, int error, atomic = 1; void *addr; - error = ops->pin(pipe, buf); + error = ops->confirm(pipe, buf); if (error) goto out; diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c index 44649981bbc..867f42b0203 100644 --- a/fs/qnx4/file.c +++ b/fs/qnx4/file.c @@ -25,7 +25,7 @@ const struct file_operations qnx4_file_operations = .read = do_sync_read, .aio_read = generic_file_aio_read, .mmap = generic_file_mmap, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, #ifdef CONFIG_QNX4FS_RW .write = do_sync_write, .aio_write = generic_file_aio_write, diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c index 2f14774a124..97bdc0b2f9d 100644 --- a/fs/ramfs/file-mmu.c +++ b/fs/ramfs/file-mmu.c @@ -41,7 +41,7 @@ const struct file_operations ramfs_file_operations = { .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, .fsync = simple_sync_file, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, .llseek = generic_file_llseek, }; diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 5d258c40a2f..cad2b7ace63 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -42,7 +42,7 @@ const struct file_operations ramfs_file_operations = { .write = do_sync_write, .aio_write = generic_file_aio_write, .fsync = simple_sync_file, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, .llseek = generic_file_llseek, }; diff --git a/fs/read_write.c b/fs/read_write.c index 4d03008f015..507ddff48a9 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/syscalls.h> #include <linux/pagemap.h> +#include <linux/splice.h> #include "read_write.h" #include <asm/uaccess.h> @@ -25,7 +26,7 @@ const struct file_operations generic_ro_fops = { .read = do_sync_read, .aio_read = generic_file_aio_read, .mmap = generic_file_readonly_mmap, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; EXPORT_SYMBOL(generic_ro_fops); @@ -708,7 +709,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, struct inode * in_inode, * out_inode; loff_t pos; ssize_t retval; - int fput_needed_in, fput_needed_out; + int fput_needed_in, fput_needed_out, fl; /* * Get input file, and verify that it is ok.. @@ -723,7 +724,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, in_inode = in_file->f_path.dentry->d_inode; if (!in_inode) goto fput_in; - if (!in_file->f_op || !in_file->f_op->sendfile) + if (!in_file->f_op || !in_file->f_op->splice_read) goto fput_in; retval = -ESPIPE; if (!ppos) @@ -776,7 +777,18 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, count = max - pos; } - retval = in_file->f_op->sendfile(in_file, ppos, count, file_send_actor, out_file); + fl = 0; +#if 0 + /* + * We need to debate whether we can enable this or not. The + * man page documents EAGAIN return for the output at least, + * and the application is arguably buggy if it doesn't expect + * EAGAIN on a non-blocking file descriptor. + */ + if (in_file->f_flags & O_NONBLOCK) + fl = SPLICE_F_NONBLOCK; +#endif + retval = do_splice_direct(in_file, ppos, out_file, count, fl); if (retval > 0) { add_rchar(current, retval); diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 9e451a68580..30eebfb1b2d 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -1531,7 +1531,6 @@ const struct file_operations reiserfs_file_operations = { .open = generic_file_open, .release = reiserfs_file_release, .fsync = reiserfs_sync_file, - .sendfile = generic_file_sendfile, .aio_read = generic_file_aio_read, .aio_write = generic_file_aio_write, .splice_read = generic_file_splice_read, diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index aea3f8aa54c..c5d78a7e492 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -262,8 +262,9 @@ out: } static ssize_t -smb_file_sendfile(struct file *file, loff_t *ppos, - size_t count, read_actor_t actor, void *target) +smb_file_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t count, + unsigned int flags) { struct dentry *dentry = file->f_path.dentry; ssize_t status; @@ -277,7 +278,7 @@ smb_file_sendfile(struct file *file, loff_t *ppos, DENTRY_PATH(dentry), status); goto out; } - status = generic_file_sendfile(file, ppos, count, actor, target); + status = generic_file_splice_read(file, ppos, pipe, count, flags); out: return status; } @@ -416,7 +417,7 @@ const struct file_operations smb_file_operations = .open = smb_file_open, .release = smb_file_release, .fsync = smb_fsync, - .sendfile = smb_file_sendfile, + .splice_read = smb_file_splice_read, }; const struct inode_operations smb_file_inode_operations = diff --git a/fs/splice.c b/fs/splice.c index e7d7080de2f..ed2ce995475 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -20,7 +20,7 @@ #include <linux/fs.h> #include <linux/file.h> #include <linux/pagemap.h> -#include <linux/pipe_fs_i.h> +#include <linux/splice.h> #include <linux/mm_inline.h> #include <linux/swap.h> #include <linux/writeback.h> @@ -29,22 +29,6 @@ #include <linux/syscalls.h> #include <linux/uio.h> -struct partial_page { - unsigned int offset; - unsigned int len; -}; - -/* - * Passed to splice_to_pipe - */ -struct splice_pipe_desc { - struct page **pages; /* page map */ - struct partial_page *partial; /* pages[] may not be contig */ - int nr_pages; /* number of pages in map */ - unsigned int flags; /* splice flags */ - const struct pipe_buf_operations *ops;/* ops associated with output pipe */ -}; - /* * Attempt to steal a page from a pipe buffer. This should perhaps go into * a vm helper function, it's already simplified quite a bit by the @@ -101,8 +85,12 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, buf->flags &= ~PIPE_BUF_FLAG_LRU; } -static int page_cache_pipe_buf_pin(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +/* + * Check whether the contents of buf is OK to access. Since the content + * is a page cache page, IO may be in flight. + */ +static int page_cache_pipe_buf_confirm(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { struct page *page = buf->page; int err; @@ -143,7 +131,7 @@ static const struct pipe_buf_operations page_cache_pipe_buf_ops = { .can_merge = 0, .map = generic_pipe_buf_map, .unmap = generic_pipe_buf_unmap, - .pin = page_cache_pipe_buf_pin, + .confirm = page_cache_pipe_buf_confirm, .release = page_cache_pipe_buf_release, .steal = page_cache_pipe_buf_steal, .get = generic_pipe_buf_get, @@ -163,18 +151,25 @@ static const struct pipe_buf_operations user_page_pipe_buf_ops = { .can_merge = 0, .map = generic_pipe_buf_map, .unmap = generic_pipe_buf_unmap, - .pin = generic_pipe_buf_pin, + .confirm = generic_pipe_buf_confirm, .release = page_cache_pipe_buf_release, .steal = user_page_pipe_buf_steal, .get = generic_pipe_buf_get, }; -/* - * Pipe output worker. This sets up our pipe format with the page cache - * pipe buffer operations. Otherwise very similar to the regular pipe_writev(). +/** + * splice_to_pipe - fill passed data into a pipe + * @pipe: pipe to fill + * @spd: data to fill + * + * Description: + * @spd contains a map of pages and len/offset tupples, a long with + * the struct pipe_buf_operations associated with these pages. This + * function will link that data to the pipe. + * */ -static ssize_t splice_to_pipe(struct pipe_inode_info *pipe, - struct splice_pipe_desc *spd) +ssize_t splice_to_pipe(struct pipe_inode_info *pipe, + struct splice_pipe_desc *spd) { unsigned int spd_pages = spd->nr_pages; int ret, do_wakeup, page_nr; @@ -201,6 +196,7 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe, buf->page = spd->pages[page_nr]; buf->offset = spd->partial[page_nr].offset; buf->len = spd->partial[page_nr].len; + buf->private = spd->partial[page_nr].private; buf->ops = spd->ops; if (spd->flags & SPLICE_F_GIFT) buf->flags |= PIPE_BUF_FLAG_GIFT; @@ -296,19 +292,15 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, page_cache_readahead(mapping, &in->f_ra, in, index, nr_pages); /* - * Now fill in the holes: - */ - error = 0; - - /* * Lookup the (hopefully) full range of pages we need. */ spd.nr_pages = find_get_pages_contig(mapping, index, nr_pages, pages); /* * If find_get_pages_contig() returned fewer pages than we needed, - * allocate the rest. + * allocate the rest and fill in the holes. */ + error = 0; index += spd.nr_pages; while (spd.nr_pages < nr_pages) { /* @@ -470,11 +462,16 @@ fill_it: /** * generic_file_splice_read - splice data from file to a pipe * @in: file to splice from + * @ppos: position in @in * @pipe: pipe to splice to * @len: number of bytes to splice * @flags: splice modifier flags * - * Will read pages from given file and fill them into a pipe. + * Description: + * Will read pages from given file and fill them into a pipe. Can be + * used as long as the address_space operations for the source implements + * a readpage() hook. + * */ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, @@ -528,11 +525,11 @@ EXPORT_SYMBOL(generic_file_splice_read); static int pipe_to_sendpage(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct splice_desc *sd) { - struct file *file = sd->file; + struct file *file = sd->u.file; loff_t pos = sd->pos; int ret, more; - ret = buf->ops->pin(pipe, buf); + ret = buf->ops->confirm(pipe, buf); if (!ret) { more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; @@ -566,7 +563,7 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe, static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct splice_desc *sd) { - struct file *file = sd->file; + struct file *file = sd->u.file; struct address_space *mapping = file->f_mapping; unsigned int offset, this_len; struct page *page; @@ -576,7 +573,7 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, /* * make sure the data in this buffer is uptodate */ - ret = buf->ops->pin(pipe, buf); + ret = buf->ops->confirm(pipe, buf); if (unlikely(ret)) return ret; @@ -663,36 +660,37 @@ out_ret: return ret; } -/* - * Pipe input worker. Most of this logic works like a regular pipe, the - * key here is the 'actor' worker passed in that actually moves the data - * to the wanted destination. See pipe_to_file/pipe_to_sendpage above. +/** + * __splice_from_pipe - splice data from a pipe to given actor + * @pipe: pipe to splice from + * @sd: information to @actor + * @actor: handler that splices the data + * + * Description: + * This function does little more than loop over the pipe and call + * @actor to do the actual moving of a single struct pipe_buffer to + * the desired destination. See pipe_to_file, pipe_to_sendpage, or + * pipe_to_user. + * */ -ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, - struct file *out, loff_t *ppos, size_t len, - unsigned int flags, splice_actor *actor) +ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd, + splice_actor *actor) { int ret, do_wakeup, err; - struct splice_desc sd; ret = 0; do_wakeup = 0; - sd.total_len = len; - sd.flags = flags; - sd.file = out; - sd.pos = *ppos; - for (;;) { if (pipe->nrbufs) { struct pipe_buffer *buf = pipe->bufs + pipe->curbuf; const struct pipe_buf_operations *ops = buf->ops; - sd.len = buf->len; - if (sd.len > sd.total_len) - sd.len = sd.total_len; + sd->len = buf->len; + if (sd->len > sd->total_len) + sd->len = sd->total_len; - err = actor(pipe, buf, &sd); + err = actor(pipe, buf, sd); if (err <= 0) { if (!ret && err != -ENODATA) ret = err; @@ -704,10 +702,10 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, buf->offset += err; buf->len -= err; - sd.len -= err; - sd.pos += err; - sd.total_len -= err; - if (sd.len) + sd->len -= err; + sd->pos += err; + sd->total_len -= err; + if (sd->len) continue; if (!buf->len) { @@ -719,7 +717,7 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, do_wakeup = 1; } - if (!sd.total_len) + if (!sd->total_len) break; } @@ -732,7 +730,7 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, break; } - if (flags & SPLICE_F_NONBLOCK) { + if (sd->flags & SPLICE_F_NONBLOCK) { if (!ret) ret = -EAGAIN; break; @@ -766,12 +764,32 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, } EXPORT_SYMBOL(__splice_from_pipe); +/** + * splice_from_pipe - splice data from a pipe to a file + * @pipe: pipe to splice from + * @out: file to splice to + * @ppos: position in @out + * @len: how many bytes to splice + * @flags: splice modifier flags + * @actor: handler that splices the data + * + * Description: + * See __splice_from_pipe. This function locks the input and output inodes, + * otherwise it's identical to __splice_from_pipe(). + * + */ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags, splice_actor *actor) { ssize_t ret; struct inode *inode = out->f_mapping->host; + struct splice_desc sd = { + .total_len = len, + .flags = flags, + .pos = *ppos, + .u.file = out, + }; /* * The actor worker might be calling ->prepare_write and @@ -780,7 +798,7 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, * pipe->inode, we have to order lock acquiry here. */ inode_double_lock(inode, pipe->inode); - ret = __splice_from_pipe(pipe, out, ppos, len, flags, actor); + ret = __splice_from_pipe(pipe, &sd, actor); inode_double_unlock(inode, pipe->inode); return ret; @@ -790,12 +808,14 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, * generic_file_splice_write_nolock - generic_file_splice_write without mutexes * @pipe: pipe info * @out: file to write to + * @ppos: position in @out * @len: number of bytes to splice * @flags: splice modifier flags * - * Will either move or copy pages (determined by @flags options) from - * the given pipe inode to the given file. The caller is responsible - * for acquiring i_mutex on both inodes. + * Description: + * Will either move or copy pages (determined by @flags options) from + * the given pipe inode to the given file. The caller is responsible + * for acquiring i_mutex on both inodes. * */ ssize_t @@ -804,6 +824,12 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out, { struct address_space *mapping = out->f_mapping; struct inode *inode = mapping->host; + struct splice_desc sd = { + .total_len = len, + .flags = flags, + .pos = *ppos, + .u.file = out, + }; ssize_t ret; int err; @@ -811,7 +837,7 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out, if (unlikely(err)) return err; - ret = __splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_file); + ret = __splice_from_pipe(pipe, &sd, pipe_to_file); if (ret > 0) { unsigned long nr_pages; @@ -841,11 +867,13 @@ EXPORT_SYMBOL(generic_file_splice_write_nolock); * generic_file_splice_write - splice data from a pipe to a file * @pipe: pipe info * @out: file to write to + * @ppos: position in @out * @len: number of bytes to splice * @flags: splice modifier flags * - * Will either move or copy pages (determined by @flags options) from - * the given pipe inode to the given file. + * Description: + * Will either move or copy pages (determined by @flags options) from + * the given pipe inode to the given file. * */ ssize_t @@ -896,13 +924,15 @@ EXPORT_SYMBOL(generic_file_splice_write); /** * generic_splice_sendpage - splice data from a pipe to a socket - * @inode: pipe inode + * @pipe: pipe to splice from * @out: socket to write to + * @ppos: position in @out * @len: number of bytes to splice * @flags: splice modifier flags * - * Will send @len bytes from the pipe to a network socket. No data copying - * is involved. + * Description: + * Will send @len bytes from the pipe to a network socket. No data copying + * is involved. * */ ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out, @@ -956,14 +986,27 @@ static long do_splice_to(struct file *in, loff_t *ppos, return in->f_op->splice_read(in, ppos, pipe, len, flags); } -long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, - size_t len, unsigned int flags) +/** + * splice_direct_to_actor - splices data directly between two non-pipes + * @in: file to splice from + * @sd: actor information on where to splice to + * @actor: handles the data splicing + * + * Description: + * This is a special case helper to splice directly between two + * points, without requiring an explicit pipe. Internally an allocated + * pipe is cached in the process, and reused during the life time of + * that process. + * + */ +ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, + splice_direct_actor *actor) { struct pipe_inode_info *pipe; long ret, bytes; - loff_t out_off; umode_t i_mode; - int i; + size_t len; + int i, flags; /* * We require the input being a regular file, as we don't want to @@ -999,7 +1042,13 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, */ ret = 0; bytes = 0; - out_off = 0; + len = sd->total_len; + flags = sd->flags; + + /* + * Don't block on output, we have to drain the direct pipe. + */ + sd->flags &= ~SPLICE_F_NONBLOCK; while (len) { size_t read_len, max_read_len; @@ -1009,19 +1058,19 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, */ max_read_len = min(len, (size_t)(PIPE_BUFFERS*PAGE_SIZE)); - ret = do_splice_to(in, ppos, pipe, max_read_len, flags); + ret = do_splice_to(in, &sd->pos, pipe, max_read_len, flags); if (unlikely(ret < 0)) goto out_release; read_len = ret; + sd->total_len = read_len; /* * NOTE: nonblocking mode only applies to the input. We * must not do the output in nonblocking mode as then we * could get stuck data in the internal pipe: */ - ret = do_splice_from(pipe, out, &out_off, read_len, - flags & ~SPLICE_F_NONBLOCK); + ret = actor(pipe, sd); if (unlikely(ret < 0)) goto out_release; @@ -1066,6 +1115,48 @@ out_release: return bytes; return ret; + +} +EXPORT_SYMBOL(splice_direct_to_actor); + +static int direct_splice_actor(struct pipe_inode_info *pipe, + struct splice_desc *sd) +{ + struct file *file = sd->u.file; + + return do_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags); +} + +/** + * do_splice_direct - splices data directly between two files + * @in: file to splice from + * @ppos: input file offset + * @out: file to splice to + * @len: number of bytes to splice + * @flags: splice modifier flags + * + * Description: + * For use by do_sendfile(). splice can easily emulate sendfile, but + * doing it in the application would incur an extra system call + * (splice in + splice out, as compared to just sendfile()). So this helper + * can splice directly through a process-private pipe. + * + */ +long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, + size_t len, unsigned int flags) +{ + struct splice_desc sd = { + .len = len, + .total_len = len, + .flags = flags, + .pos = *ppos, + .u.file = out, + }; + size_t ret; + + ret = splice_direct_to_actor(in, &sd, direct_splice_actor); + *ppos = sd.pos; + return ret; } /* @@ -1248,28 +1339,131 @@ static int get_iovec_page_array(const struct iovec __user *iov, return error; } +static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf, + struct splice_desc *sd) +{ + char *src; + int ret; + + ret = buf->ops->confirm(pipe, buf); + if (unlikely(ret)) + return ret; + + /* + * See if we can use the atomic maps, by prefaulting in the + * pages and doing an atomic copy + */ + if (!fault_in_pages_writeable(sd->u.userptr, sd->len)) { + src = buf->ops->map(pipe, buf, 1); + ret = __copy_to_user_inatomic(sd->u.userptr, src + buf->offset, + sd->len); + buf->ops->unmap(pipe, buf, src); + if (!ret) { + ret = sd->len; + goto out; + } + } + + /* + * No dice, use slow non-atomic map and copy + */ + src = buf->ops->map(pipe, buf, 0); + + ret = sd->len; + if (copy_to_user(sd->u.userptr, src + buf->offset, sd->len)) + ret = -EFAULT; + +out: + if (ret > 0) + sd->u.userptr += ret; + buf->ops->unmap(pipe, buf, src); + return ret; +} + +/* + * For lack of a better implementation, implement vmsplice() to userspace + * as a simple copy of the pipes pages to the user iov. + */ +static long vmsplice_to_user(struct file *file, const struct iovec __user *iov, + unsigned long nr_segs, unsigned int flags) +{ + struct pipe_inode_info *pipe; + struct splice_desc sd; + ssize_t size; + int error; + long ret; + + pipe = pipe_info(file->f_path.dentry->d_inode); + if (!pipe) + return -EBADF; + + if (pipe->inode) + mutex_lock(&pipe->inode->i_mutex); + + error = ret = 0; + while (nr_segs) { + void __user *base; + size_t len; + + /* + * Get user address base and length for this iovec. + */ + error = get_user(base, &iov->iov_base); + if (unlikely(error)) + break; + error = get_user(len, &iov->iov_len); + if (unlikely(error)) + break; + + /* + * Sanity check this iovec. 0 read succeeds. + */ + if (unlikely(!len)) + break; + if (unlikely(!base)) { + error = -EFAULT; + break; + } + + sd.len = 0; + sd.total_len = len; + sd.flags = flags; + sd.u.userptr = base; + sd.pos = 0; + + size = __splice_from_pipe(pipe, &sd, pipe_to_user); + if (size < 0) { + if (!ret) + ret = size; + + break; + } + + ret += size; + + if (size < len) + break; + + nr_segs--; + iov++; + } + + if (pipe->inode) + mutex_unlock(&pipe->inode->i_mutex); + + if (!ret) + ret = error; + + return ret; +} + /* * vmsplice splices a user address range into a pipe. It can be thought of * as splice-from-memory, where the regular splice is splice-from-file (or * to file). In both cases the output is a pipe, naturally. - * - * Note that vmsplice only supports splicing _from_ user memory to a pipe, - * not the other way around. Splicing from user memory is a simple operation - * that can be supported without any funky alignment restrictions or nasty - * vm tricks. We simply map in the user memory and fill them into a pipe. - * The reverse isn't quite as easy, though. There are two possible solutions - * for that: - * - * - memcpy() the data internally, at which point we might as well just - * do a regular read() on the buffer anyway. - * - Lots of nasty vm tricks, that are neither fast nor flexible (it - * has restriction limitations on both ends of the pipe). - * - * Alas, it isn't here. - * */ -static long do_vmsplice(struct file *file, const struct iovec __user *iov, - unsigned long nr_segs, unsigned int flags) +static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov, + unsigned long nr_segs, unsigned int flags) { struct pipe_inode_info *pipe; struct page *pages[PIPE_BUFFERS]; @@ -1284,10 +1478,6 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, pipe = pipe_info(file->f_path.dentry->d_inode); if (!pipe) return -EBADF; - if (unlikely(nr_segs > UIO_MAXIOV)) - return -EINVAL; - else if (unlikely(!nr_segs)) - return 0; spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial, flags & SPLICE_F_GIFT); @@ -1297,6 +1487,22 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, return splice_to_pipe(pipe, &spd); } +/* + * Note that vmsplice only really supports true splicing _from_ user memory + * to a pipe, not the other way around. Splicing from user memory is a simple + * operation that can be supported without any funky alignment restrictions + * or nasty vm tricks. We simply map in the user memory and fill them into + * a pipe. The reverse isn't quite as easy, though. There are two possible + * solutions for that: + * + * - memcpy() the data internally, at which point we might as well just + * do a regular read() on the buffer anyway. + * - Lots of nasty vm tricks, that are neither fast nor flexible (it + * has restriction limitations on both ends of the pipe). + * + * Currently we punt and implement it as a normal copy, see pipe_to_user(). + * + */ asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov, unsigned long nr_segs, unsigned int flags) { @@ -1304,11 +1510,18 @@ asmlinkage long sys_vmsplice(int fd, const struct iovec __user *iov, long error; int fput; + if (unlikely(nr_segs > UIO_MAXIOV)) + return -EINVAL; + else if (unlikely(!nr_segs)) + return 0; + error = -EBADF; file = fget_light(fd, &fput); if (file) { if (file->f_mode & FMODE_WRITE) - error = do_vmsplice(file, iov, nr_segs, flags); + error = vmsplice_to_pipe(file, iov, nr_segs, flags); + else if (file->f_mode & FMODE_READ) + error = vmsplice_to_user(file, iov, nr_segs, flags); fput_light(file, fput); } diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 0732ddb9020..589be21d884 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -27,7 +27,7 @@ const struct file_operations sysv_file_operations = { .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, .fsync = sysv_sync_file, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; const struct inode_operations sysv_file_inode_operations = { diff --git a/fs/udf/file.c b/fs/udf/file.c index 51b5764685e..df070bee8d4 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -261,7 +261,7 @@ const struct file_operations udf_file_operations = { .aio_write = udf_file_aio_write, .release = udf_release_file, .fsync = udf_fsync_file, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; const struct inode_operations udf_file_inode_operations = { diff --git a/fs/ufs/file.c b/fs/ufs/file.c index 1e096323bad..6705d74c6d2 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -60,5 +60,5 @@ const struct file_operations ufs_file_operations = { .mmap = generic_file_mmap, .open = generic_file_open, .fsync = ufs_sync_file, - .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, }; diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index cb51dc96135..8c43cd2e237 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -124,30 +124,6 @@ xfs_file_aio_write_invis( } STATIC ssize_t -xfs_file_sendfile( - struct file *filp, - loff_t *pos, - size_t count, - read_actor_t actor, - void *target) -{ - return bhv_vop_sendfile(vn_from_inode(filp->f_path.dentry->d_inode), - filp, pos, 0, count, actor, target, NULL); -} - -STATIC ssize_t -xfs_file_sendfile_invis( - struct file *filp, - loff_t *pos, - size_t count, - read_actor_t actor, - void *target) -{ - return bhv_vop_sendfile(vn_from_inode(filp->f_path.dentry->d_inode), - filp, pos, IO_INVIS, count, actor, target, NULL); -} - -STATIC ssize_t xfs_file_splice_read( struct file *infilp, loff_t *ppos, @@ -452,7 +428,6 @@ const struct file_operations xfs_file_operations = { .write = do_sync_write, .aio_read = xfs_file_aio_read, .aio_write = xfs_file_aio_write, - .sendfile = xfs_file_sendfile, .splice_read = xfs_file_splice_read, .splice_write = xfs_file_splice_write, .unlocked_ioctl = xfs_file_ioctl, @@ -475,7 +450,6 @@ const struct file_operations xfs_invis_file_operations = { .write = do_sync_write, .aio_read = xfs_file_aio_read_invis, .aio_write = xfs_file_aio_write_invis, - .sendfile = xfs_file_sendfile_invis, .splice_read = xfs_file_splice_read_invis, .splice_write = xfs_file_splice_write_invis, .unlocked_ioctl = xfs_file_ioctl_invis, diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index 715adad7dd4..af24a457d3a 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -101,7 +101,6 @@ * Feature macros (disable/enable) */ #undef HAVE_REFCACHE /* reference cache not needed for NFS in 2.6 */ -#define HAVE_SENDFILE /* sendfile(2) exists in 2.6, but not in 2.4 */ #define HAVE_SPLICE /* a splice(2) exists in 2.6, but not in 2.4 */ #ifdef CONFIG_SMP #define HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */ diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c index ed90403f0ee..765ec16a6e3 100644 --- a/fs/xfs/linux-2.6/xfs_lrw.c +++ b/fs/xfs/linux-2.6/xfs_lrw.c @@ -287,50 +287,6 @@ xfs_read( } ssize_t -xfs_sendfile( - bhv_desc_t *bdp, - struct file *filp, - loff_t *offset, - int ioflags, - size_t count, - read_actor_t actor, - void *target, - cred_t *credp) -{ - xfs_inode_t *ip = XFS_BHVTOI(bdp); - xfs_mount_t *mp = ip->i_mount; - ssize_t ret; - - XFS_STATS_INC(xs_read_calls); - if (XFS_FORCED_SHUTDOWN(mp)) - return -EIO; - - xfs_ilock(ip, XFS_IOLOCK_SHARED); - - if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) && - (!(ioflags & IO_INVIS))) { - bhv_vrwlock_t locktype = VRWLOCK_READ; - int error; - - error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp), - *offset, count, - FILP_DELAY_FLAG(filp), &locktype); - if (error) { - xfs_iunlock(ip, XFS_IOLOCK_SHARED); - return -error; - } - } - xfs_rw_enter_trace(XFS_SENDFILE_ENTER, &ip->i_iocore, - (void *)(unsigned long)target, count, *offset, ioflags); - ret = generic_file_sendfile(filp, offset, count, actor, target); - if (ret > 0) - XFS_STATS_ADD(xs_read_bytes, ret); - - xfs_iunlock(ip, XFS_IOLOCK_SHARED); - return ret; -} - -ssize_t xfs_splice_read( bhv_desc_t *bdp, struct file *infilp, diff --git a/fs/xfs/linux-2.6/xfs_lrw.h b/fs/xfs/linux-2.6/xfs_lrw.h index 7ac51b1d216..7c60a1eed88 100644 --- a/fs/xfs/linux-2.6/xfs_lrw.h +++ b/fs/xfs/linux-2.6/xfs_lrw.h @@ -90,9 +90,6 @@ extern ssize_t xfs_read(struct bhv_desc *, struct kiocb *, extern ssize_t xfs_write(struct bhv_desc *, struct kiocb *, const struct iovec *, unsigned int, loff_t *, int, struct cred *); -extern ssize_t xfs_sendfile(struct bhv_desc *, struct file *, - loff_t *, int, size_t, read_actor_t, - void *, struct cred *); extern ssize_t xfs_splice_read(struct bhv_desc *, struct file *, loff_t *, struct pipe_inode_info *, size_t, int, int, struct cred *); diff --git a/fs/xfs/linux-2.6/xfs_vnode.h b/fs/xfs/linux-2.6/xfs_vnode.h index d1b2d01843d..013048a9264 100644 --- a/fs/xfs/linux-2.6/xfs_vnode.h +++ b/fs/xfs/linux-2.6/xfs_vnode.h @@ -139,9 +139,6 @@ typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct kiocb *, typedef ssize_t (*vop_write_t)(bhv_desc_t *, struct kiocb *, const struct iovec *, unsigned int, loff_t *, int, struct cred *); -typedef ssize_t (*vop_sendfile_t)(bhv_desc_t *, struct file *, - loff_t *, int, size_t, read_actor_t, - void *, struct cred *); typedef ssize_t (*vop_splice_read_t)(bhv_desc_t *, struct file *, loff_t *, struct pipe_inode_info *, size_t, int, int, struct cred *); @@ -206,7 +203,6 @@ typedef struct bhv_vnodeops { vop_close_t vop_close; vop_read_t vop_read; vop_write_t vop_write; - vop_sendfile_t vop_sendfile; vop_splice_read_t vop_splice_read; vop_splice_write_t vop_splice_write; vop_ioctl_t vop_ioctl; @@ -254,8 +250,6 @@ typedef struct bhv_vnodeops { VOP(vop_read, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr) #define bhv_vop_write(vp,file,iov,segs,offset,ioflags,cr) \ VOP(vop_write, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr) -#define bhv_vop_sendfile(vp,f,off,ioflags,cnt,act,targ,cr) \ - VOP(vop_sendfile, vp)(VNHEAD(vp),f,off,ioflags,cnt,act,targ,cr) #define bhv_vop_splice_read(vp,f,o,pipe,cnt,fl,iofl,cr) \ VOP(vop_splice_read, vp)(VNHEAD(vp),f,o,pipe,cnt,fl,iofl,cr) #define bhv_vop_splice_write(vp,f,o,pipe,cnt,fl,iofl,cr) \ diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index de17aed578f..70bc82f6531 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -4680,9 +4680,6 @@ bhv_vnodeops_t xfs_vnodeops = { .vop_open = xfs_open, .vop_close = xfs_close, .vop_read = xfs_read, -#ifdef HAVE_SENDFILE - .vop_sendfile = xfs_sendfile, -#endif #ifdef HAVE_SPLICE .vop_splice_read = xfs_splice_read, .vop_splice_write = xfs_splice_write, diff --git a/include/asm-s390/atomic.h b/include/asm-s390/atomic.h index c17bdbf2206..ea486952f77 100644 --- a/include/asm-s390/atomic.h +++ b/include/asm-s390/atomic.h @@ -24,7 +24,7 @@ */ typedef struct { - volatile int counter; + int counter; } __attribute__ ((aligned (4))) atomic_t; #define ATOMIC_INIT(i) { (i) } @@ -141,7 +141,7 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) #ifdef __s390x__ typedef struct { - volatile long long counter; + long long counter; } __attribute__ ((aligned (8))) atomic64_t; #define ATOMIC64_INIT(i) { (i) } diff --git a/include/asm-s390/cmb.h b/include/asm-s390/cmb.h index 241756f80df..021e7c3223e 100644 --- a/include/asm-s390/cmb.h +++ b/include/asm-s390/cmb.h @@ -88,7 +88,6 @@ extern u64 cmf_read(struct ccw_device *cdev, int index); * any **/ extern int cmf_readall(struct ccw_device *cdev, struct cmbdata*data); -extern void cmf_reset(struct ccw_device *cdev); #endif /* __KERNEL__ */ #endif /* S390_CMB_H */ diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h index 5cb480af65d..3b972d4c6b2 100644 --- a/include/asm-s390/processor.h +++ b/include/asm-s390/processor.h @@ -357,8 +357,8 @@ extern void (*s390_base_ext_handler_fn)(void); /* * CPU idle notifier chain. */ -#define CPU_IDLE 0 -#define CPU_NOT_IDLE 1 +#define S390_CPU_IDLE 0 +#define S390_CPU_NOT_IDLE 1 struct notifier_block; int register_idle_notifier(struct notifier_block *nb); diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h index 21ed6477321..cb9faf1ea5c 100644 --- a/include/asm-s390/sclp.h +++ b/include/asm-s390/sclp.h @@ -11,29 +11,6 @@ #include <linux/types.h> #include <asm/chpid.h> -struct sccb_header { - u16 length; - u8 function_code; - u8 control_mask[3]; - u16 response_code; -} __attribute__((packed)); - -#define LOADPARM_LEN 8 - -struct sclp_readinfo_sccb { - struct sccb_header header; /* 0-7 */ - u16 rnmax; /* 8-9 */ - u8 rnsize; /* 10 */ - u8 _reserved0[24 - 11]; /* 11-23 */ - u8 loadparm[LOADPARM_LEN]; /* 24-31 */ - u8 _reserved1[91 - 32]; /* 32-90 */ - u8 flags; /* 91 */ - u8 _reserved2[100 - 92]; /* 92-99 */ - u32 rnsize2; /* 100-103 */ - u64 rnmax2; /* 104-111 */ - u8 _reserved3[4096 - 112]; /* 112-4095 */ -} __attribute__((packed, aligned(4096))); - #define SCLP_CHP_INFO_MASK_SIZE 32 struct sclp_chp_info { @@ -42,12 +19,22 @@ struct sclp_chp_info { u8 configured[SCLP_CHP_INFO_MASK_SIZE]; }; -extern struct sclp_readinfo_sccb s390_readinfo_sccb; -extern void sclp_readinfo_early(void); -extern int sclp_sdias_blk_count(void); -extern int sclp_sdias_copy(void *dest, int blk_num, int nr_blks); -extern int sclp_chp_configure(struct chp_id chpid); -extern int sclp_chp_deconfigure(struct chp_id chpid); -extern int sclp_chp_read_info(struct sclp_chp_info *info); +#define LOADPARM_LEN 8 + +struct sclp_ipl_info { + int is_valid; + int has_dump; + char loadparm[LOADPARM_LEN]; +}; + +void sclp_readinfo_early(void); +void sclp_facilities_detect(void); +unsigned long long sclp_memory_detect(void); +int sclp_sdias_blk_count(void); +int sclp_sdias_copy(void *dest, int blk_num, int nr_blks); +int sclp_chp_configure(struct chp_id chpid); +int sclp_chp_deconfigure(struct chp_id chpid); +int sclp_chp_read_info(struct sclp_chp_info *info); +void sclp_get_ipl_info(struct sclp_ipl_info *info); #endif /* _ASM_S390_SCLP_H */ diff --git a/include/asm-s390/sfp-machine.h b/include/asm-s390/sfp-machine.h index 8ca8c77b2d0..4e16aede4b0 100644 --- a/include/asm-s390/sfp-machine.h +++ b/include/asm-s390/sfp-machine.h @@ -27,9 +27,9 @@ #define _FP_W_TYPE_SIZE 32 -#define _FP_W_TYPE unsigned long -#define _FP_WS_TYPE signed long -#define _FP_I_TYPE long +#define _FP_W_TYPE unsigned int +#define _FP_WS_TYPE signed int +#define _FP_I_TYPE int #define _FP_MUL_MEAT_S(R,X,Y) \ _FP_MUL_MEAT_1_wide(_FP_WFRACBITS_S,R,X,Y,umul_ppmm) diff --git a/include/asm-s390/sfp-util.h b/include/asm-s390/sfp-util.h index 8cabcd23d97..0addc6466d9 100644 --- a/include/asm-s390/sfp-util.h +++ b/include/asm-s390/sfp-util.h @@ -51,6 +51,16 @@ wl = __wl; \ }) +#ifdef __s390x__ +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { unsigned long __n; \ + unsigned int __r, __d; \ + __n = ((unsigned long)(n1) << 32) + n0; \ + __d = (d); \ + (q) = __n / __d; \ + (r) = __n % __d; \ + } while (0) +#else #define udiv_qrnnd(q, r, n1, n0, d) \ do { unsigned int __r; \ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \ @@ -58,6 +68,7 @@ } while (0) extern unsigned long __udiv_qrnnd (unsigned int *, unsigned int, unsigned int , unsigned int); +#endif #define UDIV_NEEDS_NORMALIZATION 0 diff --git a/include/linux/Kbuild b/include/linux/Kbuild index f317c270d4b..afae306b177 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -49,6 +49,7 @@ header-y += consolemap.h header-y += const.h header-y += cycx_cfm.h header-y += dlm_device.h +header-y += dlm_netlink.h header-y += dm-ioctl.h header-y += dn.h header-y += dqblk_v1.h diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index db5b00a792f..fae138bd220 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -868,11 +868,6 @@ void kblockd_flush_work(struct work_struct *work); */ #define buffer_heads_over_limit 0 -static inline long blk_congestion_wait(int rw, long timeout) -{ - return io_schedule_timeout(timeout); -} - static inline long nr_blockdev_pages(void) { return 0; diff --git a/include/linux/dlm.h b/include/linux/dlm.h index 1b1dcb9a40b..be9d278761e 100644 --- a/include/linux/dlm.h +++ b/include/linux/dlm.h @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -85,7 +85,11 @@ * Only relevant to locks originating in userspace. A persistent lock will not * be removed if the process holding the lock exits. * - * DLM_LKF_NODLKWT + * DLM_LKF_NODLCKWT + * + * Do not cancel the lock if it gets into conversion deadlock. + * Exclude this lock from being monitored due to DLM_LSFL_TIMEWARN. + * * DLM_LKF_NODLCKBLK * * net yet implemented @@ -149,6 +153,7 @@ #define DLM_LKF_ALTPR 0x00008000 #define DLM_LKF_ALTCW 0x00010000 #define DLM_LKF_FORCEUNLOCK 0x00020000 +#define DLM_LKF_TIMEOUT 0x00040000 /* * Some return codes that are not in errno.h @@ -199,11 +204,12 @@ struct dlm_lksb { char * sb_lvbptr; }; +#define DLM_LSFL_NODIR 0x00000001 +#define DLM_LSFL_TIMEWARN 0x00000002 +#define DLM_LSFL_FS 0x00000004 #ifdef __KERNEL__ -#define DLM_LSFL_NODIR 0x00000001 - /* * dlm_new_lockspace * diff --git a/include/linux/dlm_device.h b/include/linux/dlm_device.h index c2735cab2eb..9642277a152 100644 --- a/include/linux/dlm_device.h +++ b/include/linux/dlm_device.h @@ -2,7 +2,7 @@ ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions @@ -18,21 +18,24 @@ #define DLM_USER_LVB_LEN 32 /* Version of the device interface */ -#define DLM_DEVICE_VERSION_MAJOR 5 -#define DLM_DEVICE_VERSION_MINOR 1 +#define DLM_DEVICE_VERSION_MAJOR 6 +#define DLM_DEVICE_VERSION_MINOR 0 #define DLM_DEVICE_VERSION_PATCH 0 /* struct passed to the lock write */ struct dlm_lock_params { __u8 mode; __u8 namelen; - __u16 flags; + __u16 unused; + __u32 flags; __u32 lkid; __u32 parent; - void __user *castparam; + __u64 xid; + __u64 timeout; + void __user *castparam; void __user *castaddr; void __user *bastparam; - void __user *bastaddr; + void __user *bastaddr; struct dlm_lksb __user *lksb; char lvb[DLM_USER_LVB_LEN]; char name[0]; @@ -62,9 +65,15 @@ struct dlm_write_request { } i; }; +struct dlm_device_version { + __u32 version[3]; +}; + /* struct read from the "device" fd, consists mainly of userspace pointers for the library to use */ + struct dlm_lock_result { + __u32 version[3]; __u32 length; void __user * user_astaddr; void __user * user_astparam; @@ -83,6 +92,7 @@ struct dlm_lock_result { #define DLM_USER_CREATE_LOCKSPACE 4 #define DLM_USER_REMOVE_LOCKSPACE 5 #define DLM_USER_PURGE 6 +#define DLM_USER_DEADLOCK 7 /* Arbitrary length restriction */ #define MAX_LS_NAME_LEN 64 diff --git a/include/linux/dlm_netlink.h b/include/linux/dlm_netlink.h new file mode 100644 index 00000000000..19276332707 --- /dev/null +++ b/include/linux/dlm_netlink.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + */ + +#ifndef _DLM_NETLINK_H +#define _DLM_NETLINK_H + +enum { + DLM_STATUS_WAITING = 1, + DLM_STATUS_GRANTED = 2, + DLM_STATUS_CONVERT = 3, +}; + +#define DLM_LOCK_DATA_VERSION 1 + +struct dlm_lock_data { + uint16_t version; + uint32_t lockspace_id; + int nodeid; + int ownpid; + uint32_t id; + uint32_t remid; + uint64_t xid; + int8_t status; + int8_t grmode; + int8_t rqmode; + unsigned long timestamp; + int resource_namelen; + char resource_name[DLM_RESNAME_MAXLEN]; +}; + +enum { + DLM_CMD_UNSPEC = 0, + DLM_CMD_HELLO, /* user->kernel */ + DLM_CMD_TIMEOUT, /* kernel->user */ + __DLM_CMD_MAX, +}; + +#define DLM_CMD_MAX (__DLM_CMD_MAX - 1) + +enum { + DLM_TYPE_UNSPEC = 0, + DLM_TYPE_LOCK, + __DLM_TYPE_MAX, +}; + +#define DLM_TYPE_MAX (__DLM_TYPE_MAX - 1) + +#define DLM_GENL_VERSION 0x1 +#define DLM_GENL_NAME "DLM" + +#endif /* _DLM_NETLINK_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 6a41f4cab14..4f0b3bf5983 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1054,7 +1054,7 @@ struct block_device_operations { }; /* - * "descriptor" for what we're up to with a read for sendfile(). + * "descriptor" for what we're up to with a read. * This allows us to use the same read code yet * have multiple different users of the data that * we read from a file. @@ -1105,7 +1105,6 @@ struct file_operations { int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); - ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); @@ -1762,7 +1761,6 @@ extern ssize_t generic_file_buffered_write(struct kiocb *, const struct iovec *, unsigned long, loff_t, loff_t *, size_t, ssize_t); extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); -extern ssize_t generic_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *); extern void do_generic_mapping_read(struct address_space *mapping, struct file_ra_state *, struct file *, loff_t *, read_descriptor_t *, read_actor_t); @@ -1792,9 +1790,6 @@ extern int nonseekable_open(struct inode * inode, struct file * filp); #ifdef CONFIG_FS_XIP extern ssize_t xip_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); -extern ssize_t xip_file_sendfile(struct file *in_file, loff_t *ppos, - size_t count, read_actor_t actor, - void *target); extern int xip_file_mmap(struct file * file, struct vm_area_struct * vma); extern ssize_t xip_file_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h index 8b7e4c1e32a..a44a6a078f0 100644 --- a/include/linux/gfs2_ondisk.h +++ b/include/linux/gfs2_ondisk.h @@ -54,18 +54,6 @@ struct gfs2_inum { __be64 no_addr; }; -struct gfs2_inum_host { - __u64 no_formal_ino; - __u64 no_addr; -}; - -static inline int gfs2_inum_equal(const struct gfs2_inum_host *ino1, - const struct gfs2_inum_host *ino2) -{ - return ino1->no_formal_ino == ino2->no_formal_ino && - ino1->no_addr == ino2->no_addr; -} - /* * Generic metadata head structure * Every inplace buffer logged in the journal must start with this. @@ -94,12 +82,6 @@ struct gfs2_meta_header { __be32 __pad1; /* Was incarnation number in gfs1 */ }; -struct gfs2_meta_header_host { - __u32 mh_magic; - __u32 mh_type; - __u32 mh_format; -}; - /* * super-block structure * @@ -139,23 +121,6 @@ struct gfs2_sb { /* In gfs1, quota and license dinodes followed */ }; -struct gfs2_sb_host { - struct gfs2_meta_header_host sb_header; - - __u32 sb_fs_format; - __u32 sb_multihost_format; - - __u32 sb_bsize; - __u32 sb_bsize_shift; - - struct gfs2_inum_host sb_master_dir; /* Was jindex dinode in gfs1 */ - struct gfs2_inum_host sb_root_dir; - - char sb_lockproto[GFS2_LOCKNAME_LEN]; - char sb_locktable[GFS2_LOCKNAME_LEN]; - /* In gfs1, quota and license dinodes followed */ -}; - /* * resource index structure */ @@ -173,14 +138,6 @@ struct gfs2_rindex { __u8 ri_reserved[64]; }; -struct gfs2_rindex_host { - __u64 ri_addr; /* grp block disk address */ - __u64 ri_data0; /* first data location */ - __u32 ri_length; /* length of rgrp header in fs blocks */ - __u32 ri_data; /* num of data blocks in rgrp */ - __u32 ri_bitbytes; /* number of bytes in data bitmaps */ -}; - /* * resource group header structure */ @@ -212,13 +169,6 @@ struct gfs2_rgrp { __u8 rg_reserved[80]; /* Several fields from gfs1 now reserved */ }; -struct gfs2_rgrp_host { - __u32 rg_flags; - __u32 rg_free; - __u32 rg_dinodes; - __u64 rg_igeneration; -}; - /* * quota structure */ @@ -230,12 +180,6 @@ struct gfs2_quota { __u8 qu_reserved[64]; }; -struct gfs2_quota_host { - __u64 qu_limit; - __u64 qu_warn; - __u64 qu_value; -}; - /* * dinode structure */ @@ -315,29 +259,11 @@ struct gfs2_dinode { struct gfs2_inum __pad4; /* Unused even in current gfs1 */ __be64 di_eattr; /* extended attribute block number */ + __be32 di_atime_nsec; /* nsec portion of atime */ + __be32 di_mtime_nsec; /* nsec portion of mtime */ + __be32 di_ctime_nsec; /* nsec portion of ctime */ - __u8 di_reserved[56]; -}; - -struct gfs2_dinode_host { - __u64 di_size; /* number of bytes in file */ - __u64 di_blocks; /* number of blocks in file */ - - /* This section varies from gfs1. Padding added to align with - * remainder of dinode - */ - __u64 di_goal_meta; /* rgrp to alloc from next */ - __u64 di_goal_data; /* data block goal */ - __u64 di_generation; /* generation number for NFS */ - - __u32 di_flags; /* GFS2_DIF_... */ - __u16 di_height; /* height of metadata */ - - /* These only apply to directories */ - __u16 di_depth; /* Number of bits in the table */ - __u32 di_entries; /* The number of entries in the directory */ - - __u64 di_eattr; /* extended attribute block number */ + __u8 di_reserved[44]; }; /* @@ -414,16 +340,6 @@ struct gfs2_log_header { __be32 lh_hash; }; -struct gfs2_log_header_host { - struct gfs2_meta_header_host lh_header; - - __u64 lh_sequence; /* Sequence number of this transaction */ - __u32 lh_flags; /* GFS2_LOG_HEAD_... */ - __u32 lh_tail; /* Block number of log tail */ - __u32 lh_blkno; - __u32 lh_hash; -}; - /* * Log type descriptor */ @@ -464,11 +380,6 @@ struct gfs2_inum_range { __be64 ir_length; }; -struct gfs2_inum_range_host { - __u64 ir_start; - __u64 ir_length; -}; - /* * Statfs change * Describes an change to the pool of free and allocated @@ -481,12 +392,6 @@ struct gfs2_statfs_change { __be64 sc_dinodes; }; -struct gfs2_statfs_change_host { - __u64 sc_total; - __u64 sc_free; - __u64 sc_dinodes; -}; - /* * Quota change * Describes an allocation change for a particular @@ -501,39 +406,12 @@ struct gfs2_quota_change { __be32 qc_id; }; -struct gfs2_quota_change_host { - __u64 qc_change; - __u32 qc_flags; /* GFS2_QCF_... */ - __u32 qc_id; +struct gfs2_quota_lvb { + __be32 qb_magic; + __u32 __pad; + __be64 qb_limit; /* Hard limit of # blocks to alloc */ + __be64 qb_warn; /* Warn user when alloc is above this # */ + __be64 qb_value; /* Current # blocks allocated */ }; -#ifdef __KERNEL__ -/* Translation functions */ - -extern void gfs2_inum_in(struct gfs2_inum_host *no, const void *buf); -extern void gfs2_inum_out(const struct gfs2_inum_host *no, void *buf); -extern void gfs2_sb_in(struct gfs2_sb_host *sb, const void *buf); -extern void gfs2_rindex_in(struct gfs2_rindex_host *ri, const void *buf); -extern void gfs2_rindex_out(const struct gfs2_rindex_host *ri, void *buf); -extern void gfs2_rgrp_in(struct gfs2_rgrp_host *rg, const void *buf); -extern void gfs2_rgrp_out(const struct gfs2_rgrp_host *rg, void *buf); -extern void gfs2_quota_in(struct gfs2_quota_host *qu, const void *buf); -struct gfs2_inode; -extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf); -extern void gfs2_ea_header_in(struct gfs2_ea_header *ea, const void *buf); -extern void gfs2_ea_header_out(const struct gfs2_ea_header *ea, void *buf); -extern void gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf); -extern void gfs2_inum_range_in(struct gfs2_inum_range_host *ir, const void *buf); -extern void gfs2_inum_range_out(const struct gfs2_inum_range_host *ir, void *buf); -extern void gfs2_statfs_change_in(struct gfs2_statfs_change_host *sc, const void *buf); -extern void gfs2_statfs_change_out(const struct gfs2_statfs_change_host *sc, void *buf); -extern void gfs2_quota_change_in(struct gfs2_quota_change_host *qc, const void *buf); - -/* Printing functions */ - -extern void gfs2_rindex_print(const struct gfs2_rindex_host *ri); -extern void gfs2_dinode_print(const struct gfs2_inode *ip); - -#endif /* __KERNEL__ */ - #endif /* __GFS2_ONDISK_DOT_H__ */ diff --git a/include/linux/gpio_mouse.h b/include/linux/gpio_mouse.h new file mode 100644 index 00000000000..44ed7aa14d8 --- /dev/null +++ b/include/linux/gpio_mouse.h @@ -0,0 +1,61 @@ +/* + * Driver for simulating a mouse on GPIO lines. + * + * Copyright (C) 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _GPIO_MOUSE_H +#define _GPIO_MOUSE_H + +#define GPIO_MOUSE_POLARITY_ACT_HIGH 0x00 +#define GPIO_MOUSE_POLARITY_ACT_LOW 0x01 + +#define GPIO_MOUSE_PIN_UP 0 +#define GPIO_MOUSE_PIN_DOWN 1 +#define GPIO_MOUSE_PIN_LEFT 2 +#define GPIO_MOUSE_PIN_RIGHT 3 +#define GPIO_MOUSE_PIN_BLEFT 4 +#define GPIO_MOUSE_PIN_BMIDDLE 5 +#define GPIO_MOUSE_PIN_BRIGHT 6 +#define GPIO_MOUSE_PIN_MAX 7 + +/** + * struct gpio_mouse_platform_data + * @scan_ms: integer in ms specifying the scan periode. + * @polarity: Pin polarity, active high or low. + * @up: GPIO line for up value. + * @down: GPIO line for down value. + * @left: GPIO line for left value. + * @right: GPIO line for right value. + * @bleft: GPIO line for left button. + * @bmiddle: GPIO line for middle button. + * @bright: GPIO line for right button. + * + * This struct must be added to the platform_device in the board code. + * It is used by the gpio_mouse driver to setup GPIO lines and to + * calculate mouse movement. + */ +struct gpio_mouse_platform_data { + int scan_ms; + int polarity; + + union { + struct { + int up; + int down; + int left; + int right; + + int bleft; + int bmiddle; + int bright; + }; + int pins[GPIO_MOUSE_PIN_MAX]; + }; +}; + +#endif /* _GPIO_MOUSE_H */ diff --git a/include/linux/input.h b/include/linux/input.h index d8521c72f69..18c98b54303 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -981,15 +981,15 @@ struct input_dev { struct mutex mutex; /* serializes open and close operations */ unsigned int users; - struct class_device cdev; + struct device dev; union { /* temporarily so while we switching to struct device */ - struct device *parent; - } dev; + struct device *dev; + } cdev; struct list_head h_list; struct list_head node; }; -#define to_input_dev(d) container_of(d, struct input_dev, cdev) +#define to_input_dev(d) container_of(d, struct input_dev, dev) /* * Verify that we are in sync with input_device_id mod_devicetable.h #defines @@ -1096,22 +1096,22 @@ struct input_handle { struct list_head h_node; }; -#define to_dev(n) container_of(n,struct input_dev,node) -#define to_handler(n) container_of(n,struct input_handler,node) -#define to_handle(n) container_of(n,struct input_handle,d_node) -#define to_handle_h(n) container_of(n,struct input_handle,h_node) +#define to_dev(n) container_of(n, struct input_dev, node) +#define to_handler(n) container_of(n, struct input_handler, node) +#define to_handle(n) container_of(n, struct input_handle, d_node) +#define to_handle_h(n) container_of(n, struct input_handle, h_node) struct input_dev *input_allocate_device(void); void input_free_device(struct input_dev *dev); static inline struct input_dev *input_get_device(struct input_dev *dev) { - return to_input_dev(class_device_get(&dev->cdev)); + return to_input_dev(get_device(&dev->dev)); } static inline void input_put_device(struct input_dev *dev) { - class_device_put(&dev->cdev); + put_device(&dev->dev); } static inline void *input_get_drvdata(struct input_dev *dev) diff --git a/include/linux/ioprio.h b/include/linux/ioprio.h index 8e2042b9d47..2eaa142cd06 100644 --- a/include/linux/ioprio.h +++ b/include/linux/ioprio.h @@ -47,8 +47,10 @@ enum { #define IOPRIO_NORM (4) static inline int task_ioprio(struct task_struct *task) { - WARN_ON(!ioprio_valid(task->ioprio)); - return IOPRIO_PRIO_DATA(task->ioprio); + if (ioprio_valid(task->ioprio)) + return IOPRIO_PRIO_DATA(task->ioprio); + + return IOPRIO_NORM; } static inline int task_nice_ioprio(struct task_struct *task) diff --git a/include/linux/pda_power.h b/include/linux/pda_power.h new file mode 100644 index 00000000000..1375f15797e --- /dev/null +++ b/include/linux/pda_power.h @@ -0,0 +1,31 @@ +/* + * Common power driver for PDAs and phones with one or two external + * power supplies (AC/USB) connected to main and backup batteries, + * and optional builtin charger. + * + * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __PDA_POWER_H__ +#define __PDA_POWER_H__ + +#define PDA_POWER_CHARGE_AC (1 << 0) +#define PDA_POWER_CHARGE_USB (1 << 1) + +struct pda_power_pdata { + int (*is_ac_online)(void); + int (*is_usb_online)(void); + void (*set_charge)(int flags); + + char **supplied_to; + size_t num_supplicants; + + unsigned int wait_for_status; /* msecs, default is 500 */ + unsigned int wait_for_charger; /* msecs, default is 500 */ +}; + +#endif /* __PDA_POWER_H__ */ diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index c8884f97122..8e4120285f7 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -9,13 +9,39 @@ #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ #define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ +/** + * struct pipe_buffer - a linux kernel pipe buffer + * @page: the page containing the data for the pipe buffer + * @offset: offset of data inside the @page + * @len: length of data inside the @page + * @ops: operations associated with this buffer. See @pipe_buf_operations. + * @flags: pipe buffer flags. See above. + * @private: private data owned by the ops. + **/ struct pipe_buffer { struct page *page; unsigned int offset, len; const struct pipe_buf_operations *ops; unsigned int flags; + unsigned long private; }; +/** + * struct pipe_inode_info - a linux kernel pipe + * @wait: reader/writer wait point in case of empty/full pipe + * @nrbufs: the number of non-empty pipe buffers in this pipe + * @curbuf: the current pipe buffer entry + * @tmp_page: cached released page + * @readers: number of current readers of this pipe + * @writers: number of current writers of this pipe + * @waiting_writers: number of writers blocked waiting for room + * @r_counter: reader counter + * @w_counter: writer counter + * @fasync_readers: reader side fasync + * @fasync_writers: writer side fasync + * @inode: inode this pipe is attached to + * @bufs: the circular array of pipe buffers + **/ struct pipe_inode_info { wait_queue_head_t wait; unsigned int nrbufs, curbuf; @@ -34,22 +60,73 @@ struct pipe_inode_info { /* * Note on the nesting of these functions: * - * ->pin() + * ->confirm() * ->steal() * ... * ->map() * ... * ->unmap() * - * That is, ->map() must be called on a pinned buffer, same goes for ->steal(). + * That is, ->map() must be called on a confirmed buffer, + * same goes for ->steal(). See below for the meaning of each + * operation. Also see kerneldoc in fs/pipe.c for the pipe + * and generic variants of these hooks. */ struct pipe_buf_operations { + /* + * This is set to 1, if the generic pipe read/write may coalesce + * data into an existing buffer. If this is set to 0, a new pipe + * page segment is always used for new data. + */ int can_merge; + + /* + * ->map() returns a virtual address mapping of the pipe buffer. + * The last integer flag reflects whether this should be an atomic + * mapping or not. The atomic map is faster, however you can't take + * page faults before calling ->unmap() again. So if you need to eg + * access user data through copy_to/from_user(), then you must get + * a non-atomic map. ->map() uses the KM_USER0 atomic slot for + * atomic maps, so you can't map more than one pipe_buffer at once + * and you have to be careful if mapping another page as source + * or destination for a copy (IOW, it has to use something else + * than KM_USER0). + */ void * (*map)(struct pipe_inode_info *, struct pipe_buffer *, int); + + /* + * Undoes ->map(), finishes the virtual mapping of the pipe buffer. + */ void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *, void *); - int (*pin)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * ->confirm() verifies that the data in the pipe buffer is there + * and that the contents are good. If the pages in the pipe belong + * to a file system, we may need to wait for IO completion in this + * hook. Returns 0 for good, or a negative error value in case of + * error. + */ + int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * When the contents of this pipe buffer has been completely + * consumed by a reader, ->release() is called. + */ void (*release)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * Attempt to take ownership of the pipe buffer and its contents. + * ->steal() returns 0 for success, in which case the contents + * of the pipe (the buf->page) is locked and now completely owned + * by the caller. The page may then be transferred to a different + * mapping, the most often used case is insertion into different + * file address space cache. + */ int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); + + /* + * Get a reference to the pipe buffer. + */ void (*get)(struct pipe_inode_info *, struct pipe_buffer *); }; @@ -68,39 +145,7 @@ void __free_pipe_info(struct pipe_inode_info *); void *generic_pipe_buf_map(struct pipe_inode_info *, struct pipe_buffer *, int); void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *, void *); void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); -int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *); +int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); -/* - * splice is tied to pipes as a transport (at least for now), so we'll just - * add the splice flags here. - */ -#define SPLICE_F_MOVE (0x01) /* move pages instead of copying */ -#define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */ - /* we may still block on the fd we splice */ - /* from/to, of course */ -#define SPLICE_F_MORE (0x04) /* expect more data */ -#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */ - -/* - * Passed to the actors - */ -struct splice_desc { - unsigned int len, total_len; /* current and remaining length */ - unsigned int flags; /* splice flags */ - struct file *file; /* file to read/write */ - loff_t pos; /* file position */ -}; - -typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *, - struct splice_desc *); - -extern ssize_t splice_from_pipe(struct pipe_inode_info *, struct file *, - loff_t *, size_t, unsigned int, - splice_actor *); - -extern ssize_t __splice_from_pipe(struct pipe_inode_info *, struct file *, - loff_t *, size_t, unsigned int, - splice_actor *); - #endif diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h new file mode 100644 index 00000000000..606c0957997 --- /dev/null +++ b/include/linux/power_supply.h @@ -0,0 +1,180 @@ +/* + * Universal power supply monitor class + * + * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> + * Copyright © 2004 Szabolcs Gyurko + * Copyright © 2003 Ian Molton <spyro@f2s.com> + * + * Modified: 2004, Oct Szabolcs Gyurko + * + * You may use this code as per GPL version 2 + */ + +#ifndef __LINUX_POWER_SUPPLY_H__ +#define __LINUX_POWER_SUPPLY_H__ + +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/leds.h> + +/* + * All voltages, currents, charges, energies, time and temperatures in uV, + * µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise + * stated. It's driver's job to convert its raw values to units in which + * this class operates. + */ + +/* + * For systems where the charger determines the maximum battery capacity + * the min and max fields should be used to present these values to user + * space. Unused/unknown fields will not appear in sysfs. + */ + +enum { + POWER_SUPPLY_STATUS_UNKNOWN = 0, + POWER_SUPPLY_STATUS_CHARGING, + POWER_SUPPLY_STATUS_DISCHARGING, + POWER_SUPPLY_STATUS_NOT_CHARGING, + POWER_SUPPLY_STATUS_FULL, +}; + +enum { + POWER_SUPPLY_HEALTH_UNKNOWN = 0, + POWER_SUPPLY_HEALTH_GOOD, + POWER_SUPPLY_HEALTH_OVERHEAT, + POWER_SUPPLY_HEALTH_DEAD, + POWER_SUPPLY_HEALTH_OVERVOLTAGE, + POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, +}; + +enum { + POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0, + POWER_SUPPLY_TECHNOLOGY_NiMH, + POWER_SUPPLY_TECHNOLOGY_LION, + POWER_SUPPLY_TECHNOLOGY_LIPO, + POWER_SUPPLY_TECHNOLOGY_LiFe, + POWER_SUPPLY_TECHNOLOGY_NiCd, +}; + +enum { + POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0, + POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL, + POWER_SUPPLY_CAPACITY_LEVEL_LOW, + POWER_SUPPLY_CAPACITY_LEVEL_NORMAL, + POWER_SUPPLY_CAPACITY_LEVEL_HIGH, + POWER_SUPPLY_CAPACITY_LEVEL_FULL, +}; + +enum power_supply_property { + /* Properties of type `int' */ + POWER_SUPPLY_PROP_STATUS = 0, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_EMPTY, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_AVG, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_EMPTY, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_ENERGY_AVG, + POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_AMBIENT, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + /* Properties of type `const char *' */ + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +enum power_supply_type { + POWER_SUPPLY_TYPE_BATTERY = 0, + POWER_SUPPLY_TYPE_UPS, + POWER_SUPPLY_TYPE_MAINS, + POWER_SUPPLY_TYPE_USB, +}; + +union power_supply_propval { + int intval; + const char *strval; +}; + +struct power_supply { + const char *name; + enum power_supply_type type; + enum power_supply_property *properties; + size_t num_properties; + + char **supplied_to; + size_t num_supplicants; + + int (*get_property)(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + void (*external_power_changed)(struct power_supply *psy); + + /* For APM emulation, think legacy userspace. */ + int use_for_apm; + + /* private */ + struct device *dev; + struct work_struct changed_work; + +#ifdef CONFIG_LEDS_TRIGGERS + struct led_trigger *charging_full_trig; + char *charging_full_trig_name; + struct led_trigger *charging_trig; + char *charging_trig_name; + struct led_trigger *full_trig; + char *full_trig_name; + struct led_trigger *online_trig; + char *online_trig_name; +#endif +}; + +/* + * This is recommended structure to specify static power supply parameters. + * Generic one, parametrizable for different power supplies. Power supply + * class itself does not use it, but that's what implementing most platform + * drivers, should try reuse for consistency. + */ + +struct power_supply_info { + const char *name; + int technology; + int voltage_max_design; + int voltage_min_design; + int charge_full_design; + int charge_empty_design; + int energy_full_design; + int energy_empty_design; + int use_for_apm; +}; + +extern void power_supply_changed(struct power_supply *psy); +extern int power_supply_am_i_supplied(struct power_supply *psy); + +extern int power_supply_register(struct device *parent, + struct power_supply *psy); +extern void power_supply_unregister(struct power_supply *psy); + +/* For APM emulation, think legacy userspace. */ +extern struct class *power_supply_class; + +#endif /* __LINUX_POWER_SUPPLY_H__ */ diff --git a/include/linux/splice.h b/include/linux/splice.h new file mode 100644 index 00000000000..33e447f98a5 --- /dev/null +++ b/include/linux/splice.h @@ -0,0 +1,73 @@ +/* + * Function declerations and data structures related to the splice + * implementation. + * + * Copyright (C) 2007 Jens Axboe <jens.axboe@oracle.com> + * + */ +#ifndef SPLICE_H +#define SPLICE_H + +#include <linux/pipe_fs_i.h> + +/* + * splice is tied to pipes as a transport (at least for now), so we'll just + * add the splice flags here. + */ +#define SPLICE_F_MOVE (0x01) /* move pages instead of copying */ +#define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */ + /* we may still block on the fd we splice */ + /* from/to, of course */ +#define SPLICE_F_MORE (0x04) /* expect more data */ +#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */ + +/* + * Passed to the actors + */ +struct splice_desc { + unsigned int len, total_len; /* current and remaining length */ + unsigned int flags; /* splice flags */ + /* + * actor() private data + */ + union { + void __user *userptr; /* memory to write to */ + struct file *file; /* file to read/write */ + void *data; /* cookie */ + } u; + loff_t pos; /* file position */ +}; + +struct partial_page { + unsigned int offset; + unsigned int len; + unsigned long private; +}; + +/* + * Passed to splice_to_pipe + */ +struct splice_pipe_desc { + struct page **pages; /* page map */ + struct partial_page *partial; /* pages[] may not be contig */ + int nr_pages; /* number of pages in map */ + unsigned int flags; /* splice flags */ + const struct pipe_buf_operations *ops;/* ops associated with output pipe */ +}; + +typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *, + struct splice_desc *); +typedef int (splice_direct_actor)(struct pipe_inode_info *, + struct splice_desc *); + +extern ssize_t splice_from_pipe(struct pipe_inode_info *, struct file *, + loff_t *, size_t, unsigned int, + splice_actor *); +extern ssize_t __splice_from_pipe(struct pipe_inode_info *, + struct splice_desc *, splice_actor *); +extern ssize_t splice_to_pipe(struct pipe_inode_info *, + struct splice_pipe_desc *); +extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *, + splice_direct_actor *); + +#endif diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 4a7ae8ab6eb..129d50f2225 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -253,7 +253,7 @@ struct svc_rqst { * determine what device number * to report (real or virtual) */ - int rq_sendfile_ok; /* turned off in gss privacy + int rq_splice_ok; /* turned off in gss privacy * to prevent encrypting page * cache pages */ wait_queue_head_t rq_wait; /* synchronization */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 94bd38a6d94..56aa2ee21f1 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -729,6 +729,22 @@ static inline int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor .bcdDevice_lo = (lo), .bcdDevice_hi = (hi) /** + * USB_DEVICE_INTERFACE_PROTOCOL - macro used to describe a usb + * device with a specific interface protocol + * @vend: the 16 bit USB Vendor ID + * @prod: the 16 bit USB Product ID + * @pr: bInterfaceProtocol value + * + * This macro is used to create a struct usb_device_id that matches a + * specific interface protocol of devices. + */ +#define USB_DEVICE_INTERFACE_PROTOCOL(vend,prod,pr) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_PROTOCOL, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceProtocol = (pr) + +/** * USB_DEVICE_INFO - macro used to describe a class of usb devices * @cl: bDeviceClass value * @sc: bDeviceSubClass value diff --git a/init/Kconfig b/init/Kconfig index a9e99f8328f..d9d878a3bb4 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -686,6 +686,4 @@ config STOP_MACHINE Need stop_machine() primitive. endmenu -menu "Block layer" source "block/Kconfig" -endmenu diff --git a/kernel/relay.c b/kernel/relay.c index 95db8c79fe8..3b299fb3855 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -21,6 +21,7 @@ #include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/cpu.h> +#include <linux/splice.h> /* list of open channels, for cpu hotplug */ static DEFINE_MUTEX(relay_channels_mutex); @@ -121,6 +122,7 @@ static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size) buf->page_array[i] = alloc_page(GFP_KERNEL); if (unlikely(!buf->page_array[i])) goto depopulate; + set_page_private(buf->page_array[i], (unsigned long)buf); } mem = vmap(buf->page_array, n_pages, VM_MAP, PAGE_KERNEL); if (!mem) @@ -970,43 +972,6 @@ static int subbuf_read_actor(size_t read_start, return ret; } -/* - * subbuf_send_actor - send up to one subbuf's worth of data - */ -static int subbuf_send_actor(size_t read_start, - struct rchan_buf *buf, - size_t avail, - read_descriptor_t *desc, - read_actor_t actor) -{ - unsigned long pidx, poff; - unsigned int subbuf_pages; - int ret = 0; - - subbuf_pages = buf->chan->alloc_size >> PAGE_SHIFT; - pidx = (read_start / PAGE_SIZE) % subbuf_pages; - poff = read_start & ~PAGE_MASK; - while (avail) { - struct page *p = buf->page_array[pidx]; - unsigned int len; - - len = PAGE_SIZE - poff; - if (len > avail) - len = avail; - - len = actor(desc, p, poff, len); - if (desc->error) - break; - - avail -= len; - ret += len; - poff = 0; - pidx = (pidx + 1) % subbuf_pages; - } - - return ret; -} - typedef int (*subbuf_actor_t) (size_t read_start, struct rchan_buf *buf, size_t avail, @@ -1067,19 +1032,159 @@ static ssize_t relay_file_read(struct file *filp, NULL, &desc); } -static ssize_t relay_file_sendfile(struct file *filp, - loff_t *ppos, - size_t count, - read_actor_t actor, - void *target) +static void relay_consume_bytes(struct rchan_buf *rbuf, int bytes_consumed) { - read_descriptor_t desc; - desc.written = 0; - desc.count = count; - desc.arg.data = target; - desc.error = 0; - return relay_file_read_subbufs(filp, ppos, subbuf_send_actor, - actor, &desc); + rbuf->bytes_consumed += bytes_consumed; + + if (rbuf->bytes_consumed >= rbuf->chan->subbuf_size) { + relay_subbufs_consumed(rbuf->chan, rbuf->cpu, 1); + rbuf->bytes_consumed %= rbuf->chan->subbuf_size; + } +} + +static void relay_pipe_buf_release(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) +{ + struct rchan_buf *rbuf; + + rbuf = (struct rchan_buf *)page_private(buf->page); + relay_consume_bytes(rbuf, buf->private); +} + +static struct pipe_buf_operations relay_pipe_buf_ops = { + .can_merge = 0, + .map = generic_pipe_buf_map, + .unmap = generic_pipe_buf_unmap, + .confirm = generic_pipe_buf_confirm, + .release = relay_pipe_buf_release, + .steal = generic_pipe_buf_steal, + .get = generic_pipe_buf_get, +}; + +/** + * subbuf_splice_actor - splice up to one subbuf's worth of data + */ +static int subbuf_splice_actor(struct file *in, + loff_t *ppos, + struct pipe_inode_info *pipe, + size_t len, + unsigned int flags, + int *nonpad_ret) +{ + unsigned int pidx, poff, total_len, subbuf_pages, ret; + struct rchan_buf *rbuf = in->private_data; + unsigned int subbuf_size = rbuf->chan->subbuf_size; + size_t read_start = ((size_t)*ppos) % rbuf->chan->alloc_size; + size_t read_subbuf = read_start / subbuf_size; + size_t padding = rbuf->padding[read_subbuf]; + size_t nonpad_end = read_subbuf * subbuf_size + subbuf_size - padding; + struct page *pages[PIPE_BUFFERS]; + struct partial_page partial[PIPE_BUFFERS]; + struct splice_pipe_desc spd = { + .pages = pages, + .nr_pages = 0, + .partial = partial, + .flags = flags, + .ops = &relay_pipe_buf_ops, + }; + + if (rbuf->subbufs_produced == rbuf->subbufs_consumed) + return 0; + + /* + * Adjust read len, if longer than what is available + */ + if (len > (subbuf_size - read_start % subbuf_size)) + len = subbuf_size - read_start % subbuf_size; + + subbuf_pages = rbuf->chan->alloc_size >> PAGE_SHIFT; + pidx = (read_start / PAGE_SIZE) % subbuf_pages; + poff = read_start & ~PAGE_MASK; + + for (total_len = 0; spd.nr_pages < subbuf_pages; spd.nr_pages++) { + unsigned int this_len, this_end, private; + unsigned int cur_pos = read_start + total_len; + + if (!len) + break; + + this_len = min_t(unsigned long, len, PAGE_SIZE - poff); + private = this_len; + + spd.pages[spd.nr_pages] = rbuf->page_array[pidx]; + spd.partial[spd.nr_pages].offset = poff; + + this_end = cur_pos + this_len; + if (this_end >= nonpad_end) { + this_len = nonpad_end - cur_pos; + private = this_len + padding; + } + spd.partial[spd.nr_pages].len = this_len; + spd.partial[spd.nr_pages].private = private; + + len -= this_len; + total_len += this_len; + poff = 0; + pidx = (pidx + 1) % subbuf_pages; + + if (this_end >= nonpad_end) { + spd.nr_pages++; + break; + } + } + + if (!spd.nr_pages) + return 0; + + ret = *nonpad_ret = splice_to_pipe(pipe, &spd); + if (ret < 0 || ret < total_len) + return ret; + + if (read_start + ret == nonpad_end) + ret += padding; + + return ret; +} + +static ssize_t relay_file_splice_read(struct file *in, + loff_t *ppos, + struct pipe_inode_info *pipe, + size_t len, + unsigned int flags) +{ + ssize_t spliced; + int ret; + int nonpad_ret = 0; + + ret = 0; + spliced = 0; + + while (len) { + ret = subbuf_splice_actor(in, ppos, pipe, len, flags, &nonpad_ret); + if (ret < 0) + break; + else if (!ret) { + if (spliced) + break; + if (flags & SPLICE_F_NONBLOCK) { + ret = -EAGAIN; + break; + } + } + + *ppos += ret; + if (ret > len) + len = 0; + else + len -= ret; + spliced += nonpad_ret; + nonpad_ret = 0; + } + + if (spliced) + return spliced; + + return ret; } const struct file_operations relay_file_operations = { @@ -1089,7 +1194,7 @@ const struct file_operations relay_file_operations = { .read = relay_file_read, .llseek = no_llseek, .release = relay_file_release, - .sendfile = relay_file_sendfile, + .splice_read = relay_file_splice_read, }; EXPORT_SYMBOL_GPL(relay_file_operations); diff --git a/mm/filemap.c b/mm/filemap.c index d1d9814f99d..c6ebd9f912a 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1245,26 +1245,6 @@ int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long o return written; } -ssize_t generic_file_sendfile(struct file *in_file, loff_t *ppos, - size_t count, read_actor_t actor, void *target) -{ - read_descriptor_t desc; - - if (!count) - return 0; - - desc.written = 0; - desc.count = count; - desc.arg.data = target; - desc.error = 0; - - do_generic_file_read(in_file, ppos, &desc, actor); - if (desc.written) - return desc.written; - return desc.error; -} -EXPORT_SYMBOL(generic_file_sendfile); - static ssize_t do_readahead(struct address_space *mapping, struct file *filp, unsigned long index, unsigned long nr) diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index fa360e566d8..65ffc321f0c 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -159,28 +159,6 @@ xip_file_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) } EXPORT_SYMBOL_GPL(xip_file_read); -ssize_t -xip_file_sendfile(struct file *in_file, loff_t *ppos, - size_t count, read_actor_t actor, void *target) -{ - read_descriptor_t desc; - - if (!count) - return 0; - - desc.written = 0; - desc.count = count; - desc.arg.data = target; - desc.error = 0; - - do_xip_mapping_read(in_file->f_mapping, &in_file->f_ra, in_file, - ppos, &desc, actor); - if (desc.written) - return desc.written; - return desc.error; -} -EXPORT_SYMBOL_GPL(xip_file_sendfile); - /* * __xip_unmap is invoked from xip_unmap and * xip_write diff --git a/mm/shmem.c b/mm/shmem.c index b6aae2b3339..0493e4d0bca 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1100,9 +1100,9 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, * Normally, filepage is NULL on entry, and either found * uptodate immediately, or allocated and zeroed, or read * in under swappage, which is then assigned to filepage. - * But shmem_prepare_write passes in a locked filepage, - * which may be found not uptodate by other callers too, - * and may need to be copied from the swappage read in. + * But shmem_readpage and shmem_prepare_write pass in a locked + * filepage, which may be found not uptodate by other callers + * too, and may need to be copied from the swappage read in. */ repeat: if (!filepage) @@ -1485,9 +1485,18 @@ static const struct inode_operations shmem_symlink_inode_operations; static const struct inode_operations shmem_symlink_inline_operations; /* - * Normally tmpfs makes no use of shmem_prepare_write, but it - * lets a tmpfs file be used read-write below the loop driver. + * Normally tmpfs avoids the use of shmem_readpage and shmem_prepare_write; + * but providing them allows a tmpfs file to be used for splice, sendfile, and + * below the loop driver, in the generic fashion that many filesystems support. */ +static int shmem_readpage(struct file *file, struct page *page) +{ + struct inode *inode = page->mapping->host; + int error = shmem_getpage(inode, page->index, &page, SGP_CACHE, NULL); + unlock_page(page); + return error; +} + static int shmem_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) { @@ -1711,25 +1720,6 @@ static ssize_t shmem_file_read(struct file *filp, char __user *buf, size_t count return desc.error; } -static ssize_t shmem_file_sendfile(struct file *in_file, loff_t *ppos, - size_t count, read_actor_t actor, void *target) -{ - read_descriptor_t desc; - - if (!count) - return 0; - - desc.written = 0; - desc.count = count; - desc.arg.data = target; - desc.error = 0; - - do_shmem_file_read(in_file, ppos, &desc, actor); - if (desc.written) - return desc.written; - return desc.error; -} - static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf) { struct shmem_sb_info *sbinfo = SHMEM_SB(dentry->d_sb); @@ -2386,6 +2376,7 @@ static const struct address_space_operations shmem_aops = { .writepage = shmem_writepage, .set_page_dirty = __set_page_dirty_no_writeback, #ifdef CONFIG_TMPFS + .readpage = shmem_readpage, .prepare_write = shmem_prepare_write, .commit_write = simple_commit_write, #endif @@ -2399,7 +2390,8 @@ static const struct file_operations shmem_file_operations = { .read = shmem_file_read, .write = shmem_file_write, .fsync = simple_sync_file, - .sendfile = shmem_file_sendfile, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, #endif }; diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 099a983797d..c094583386f 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -853,7 +853,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs u32 priv_len, maj_stat; int pad, saved_len, remaining_len, offset; - rqstp->rq_sendfile_ok = 0; + rqstp->rq_splice_ok = 0; priv_len = svc_getnl(&buf->head[0]); if (rqstp->rq_deferred) { diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index e673ef99390..55ea6df069d 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -814,7 +814,7 @@ svc_process(struct svc_rqst *rqstp) rqstp->rq_res.tail[0].iov_base = NULL; rqstp->rq_res.tail[0].iov_len = 0; /* Will be turned off only in gss privacy case: */ - rqstp->rq_sendfile_ok = 1; + rqstp->rq_splice_ok = 1; /* tcp needs a space for the record length... */ if (rqstp->rq_prot == IPPROTO_TCP) svc_putnl(resv, 0); diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index 5f38f670102..a1aa89f2faf 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -118,7 +118,7 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, default: return -1; } - chip = dev->private; + chip = input_get_drvdata(dev); if (! chip || (beep = chip->beep) == NULL) return -1; @@ -239,8 +239,8 @@ int __init snd_pmac_attach_beep(struct snd_pmac *chip) input_dev->evbit[0] = BIT(EV_SND); input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); input_dev->event = snd_pmac_beep_event; - input_dev->private = chip; - input_dev->cdev.dev = &chip->pdev->dev; + input_dev->dev.parent = &chip->pdev->dev; + input_set_drvdata(input_dev, chip); beep->dev = input_dev; beep->buf = dmabuf; @@ -251,8 +251,8 @@ int __init snd_pmac_attach_beep(struct snd_pmac *chip) err = snd_ctl_add(chip->card, beep_ctl); if (err < 0) goto fail1; - - chip->beep = beep; + + chip->beep = beep; err = input_register_device(beep->dev); if (err) |