From ca9c1aaec4187fc9922cfb6b283fffef89286943 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Tue, 6 Jan 2009 20:11:51 +0000 Subject: ASoC: dapm: Allow explictly named mixer controls This patch allows you to define the mixer paths as having the same name as the paths they represent. This is required to support codecs such as the wm9705 neatly without extra controls in the alsa mixer. Signed-off-by: Ian Molton --- Documentation/sound/alsa/soc/dapm.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/alsa/soc/dapm.txt index 46f9684d0b29..9e6763264a2e 100644 --- a/Documentation/sound/alsa/soc/dapm.txt +++ b/Documentation/sound/alsa/soc/dapm.txt @@ -116,6 +116,9 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls, ARRAY_SIZE(wm8731_output_mixer_controls)), +If you dont want the mixer elements prefixed with the name of the mixer widget, +you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same +as for SND_SOC_DAPM_MIXER. 2.3 Platform/Machine domain Widgets ----------------------------------- -- cgit v1.2.3-59-g8ed1b From fbd59a8d1f7cf325fdb6828659f1fb76631e87b3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Jan 2009 21:58:08 -0800 Subject: cpumask: Use topology_core_cpumask()/topology_thread_cpumask() Impact: reduce stack usage, use new cpumask API. This actually uses topology_core_cpumask() and topology_thread_cpumask(), removing the only users of topology_core_siblings() and topology_thread_siblings() Signed-off-by: Rusty Russell Signed-off-by: Mike Travis Cc: linux-net-drivers@solarflare.com --- Documentation/cputopology.txt | 6 +++--- drivers/base/topology.c | 33 ++++++++++++++++----------------- drivers/net/sfc/efx.c | 4 ++-- include/linux/topology.h | 6 ++++++ 4 files changed, 27 insertions(+), 22 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cputopology.txt b/Documentation/cputopology.txt index 45932ec21cee..b41f3e58aefa 100644 --- a/Documentation/cputopology.txt +++ b/Documentation/cputopology.txt @@ -18,11 +18,11 @@ For an architecture to support this feature, it must define some of these macros in include/asm-XXX/topology.h: #define topology_physical_package_id(cpu) #define topology_core_id(cpu) -#define topology_thread_siblings(cpu) -#define topology_core_siblings(cpu) +#define topology_thread_cpumask(cpu) +#define topology_core_cpumask(cpu) The type of **_id is int. -The type of siblings is cpumask_t. +The type of siblings is (const) struct cpumask *. To be consistent on all architectures, include/linux/topology.h provides default definitions for any of the above macros that are diff --git a/drivers/base/topology.c b/drivers/base/topology.c index a778fb52b11f..bf6b13206d00 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -31,7 +31,10 @@ #include #include -#define define_one_ro(_name) \ +#define define_one_ro_named(_name, _func) \ +static SYSDEV_ATTR(_name, 0444, _func, NULL) + +#define define_one_ro(_name) \ static SYSDEV_ATTR(_name, 0444, show_##_name, NULL) #define define_id_show_func(name) \ @@ -42,8 +45,8 @@ static ssize_t show_##name(struct sys_device *dev, \ return sprintf(buf, "%d\n", topology_##name(cpu)); \ } -#if defined(topology_thread_siblings) || defined(topology_core_siblings) -static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf) +#if defined(topology_thread_cpumask) || defined(topology_core_cpumask) +static ssize_t show_cpumap(int type, const struct cpumask *mask, char *buf) { ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf; int n = 0; @@ -65,7 +68,7 @@ static ssize_t show_##name(struct sys_device *dev, \ struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ - return show_cpumap(0, &(topology_##name(cpu)), buf); \ + return show_cpumap(0, topology_##name(cpu), buf); \ } #define define_siblings_show_list(name) \ @@ -74,7 +77,7 @@ static ssize_t show_##name##_list(struct sys_device *dev, \ char *buf) \ { \ unsigned int cpu = dev->id; \ - return show_cpumap(1, &(topology_##name(cpu)), buf); \ + return show_cpumap(1, topology_##name(cpu), buf); \ } #else @@ -82,9 +85,7 @@ static ssize_t show_##name##_list(struct sys_device *dev, \ static ssize_t show_##name(struct sys_device *dev, \ struct sysdev_attribute *attr, char *buf) \ { \ - unsigned int cpu = dev->id; \ - cpumask_t mask = topology_##name(cpu); \ - return show_cpumap(0, &mask, buf); \ + return show_cpumap(0, topology_##name(dev->id), buf); \ } #define define_siblings_show_list(name) \ @@ -92,9 +93,7 @@ static ssize_t show_##name##_list(struct sys_device *dev, \ struct sysdev_attribute *attr, \ char *buf) \ { \ - unsigned int cpu = dev->id; \ - cpumask_t mask = topology_##name(cpu); \ - return show_cpumap(1, &mask, buf); \ + return show_cpumap(1, topology_##name(dev->id), buf); \ } #endif @@ -107,13 +106,13 @@ define_one_ro(physical_package_id); define_id_show_func(core_id); define_one_ro(core_id); -define_siblings_show_func(thread_siblings); -define_one_ro(thread_siblings); -define_one_ro(thread_siblings_list); +define_siblings_show_func(thread_cpumask); +define_one_ro_named(thread_siblings, show_thread_cpumask); +define_one_ro_named(thread_siblings_list, show_thread_cpumask_list); -define_siblings_show_func(core_siblings); -define_one_ro(core_siblings); -define_one_ro(core_siblings_list); +define_siblings_show_func(core_cpumask); +define_one_ro_named(core_siblings, show_core_cpumask); +define_one_ro_named(core_siblings_list, show_core_cpumask_list); static struct attribute *default_attrs[] = { &attr_physical_package_id.attr, diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 7673fd92eaf5..f2e56ceee0ea 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -863,8 +863,8 @@ static int efx_wanted_rx_queues(void) for_each_online_cpu(cpu) { if (!cpu_isset(cpu, core_mask)) { ++count; - cpus_or(core_mask, core_mask, - topology_core_siblings(cpu)); + cpumask_or(&core_mask, &core_mask, + topology_core_cpumask(cpu)); } } diff --git a/include/linux/topology.h b/include/linux/topology.h index e632d29f0544..a16b9e06f2e5 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -193,5 +193,11 @@ int arch_update_cpu_topology(void); #ifndef topology_core_siblings #define topology_core_siblings(cpu) cpumask_of_cpu(cpu) #endif +#ifndef topology_thread_cpumask +#define topology_thread_cpumask(cpu) cpumask_of(cpu) +#endif +#ifndef topology_core_cpumask +#define topology_core_cpumask(cpu) cpumask_of(cpu) +#endif #endif /* _LINUX_TOPOLOGY_H */ -- cgit v1.2.3-59-g8ed1b From d453379bc5d34d7f55b55931245de5ac1896fd8d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 28 Dec 2008 16:45:34 +0100 Subject: ALSA: Update description of snd_card_create() in documents Signed-off-by: Takashi Iwai --- .../sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 44 ++++++++++++---------- 1 file changed, 25 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index 87a7c07ab658..320384c1791b 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -492,9 +492,9 @@ } /* (2) */ - card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); - if (card == NULL) - return -ENOMEM; + err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); + if (err < 0) + return err; /* (3) */ err = snd_mychip_create(card, pci, &chip); @@ -590,8 +590,9 @@ @@ -809,26 +810,28 @@ As mentioned above, to create a card instance, call - snd_card_new(). + snd_card_create(). - The function takes four arguments, the card-index number, the + The function takes five arguments, the card-index number, the id string, the module pointer (usually THIS_MODULE), - and the size of extra-data space. The last argument is used to + the size of extra-data space, and the pointer to return the + card instance. The extra_size argument is used to allocate card->private_data for the chip-specific data. Note that these data - are allocated by snd_card_new(). + are allocated by snd_card_create(). @@ -915,15 +918,16 @@
- 1. Allocating via <function>snd_card_new()</function>. + 1. Allocating via <function>snd_card_create()</function>. As mentioned above, you can pass the extra-data-length - to the 4th argument of snd_card_new(), i.e. + to the 4th argument of snd_card_create(), i.e. @@ -952,8 +956,8 @@ After allocating a card instance via - snd_card_new() (with - NULL on the 4th arg), call + snd_card_create() (with + 0 on the 4th arg), call kzalloc(). @@ -961,7 +965,7 @@ @@ -5750,8 +5754,9 @@ struct _snd_pcm_runtime { .... struct snd_card *card; struct mychip *chip; + int err; .... - card = snd_card_new(index[dev], id[dev], THIS_MODULE, NULL); + err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); .... chip = kzalloc(sizeof(*chip), GFP_KERNEL); .... @@ -5763,7 +5768,7 @@ struct _snd_pcm_runtime { When you created the chip data with - snd_card_new(), it's anyway accessible + snd_card_create(), it's anyway accessible via private_data field. @@ -5775,9 +5780,10 @@ struct _snd_pcm_runtime { .... struct snd_card *card; struct mychip *chip; + int err; .... - card = snd_card_new(index[dev], id[dev], THIS_MODULE, - sizeof(struct mychip)); + err = snd_card_create(index[dev], id[dev], THIS_MODULE, + sizeof(struct mychip), &card); .... chip = card->private_data; .... -- cgit v1.2.3-59-g8ed1b From ee287587dafd77fd211e50637561224605c214b4 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 14 Jan 2009 16:07:38 -0800 Subject: bzip2/lzma: update boot protocol specification Impact: documentation Update the boot protocol specification to include the currently supported file formats and their magic numbers. Signed-off-by: H. Peter Anvin --- Documentation/x86/boot.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index fcdc62b3c3d8..9ac70ffe2b77 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -542,7 +542,10 @@ Protocol: 2.08+ The payload may be compressed. The format of both the compressed and uncompressed data should be determined using the standard magic - numbers. Currently only gzip compressed ELF is used. + numbers. The currently supported compression formats are gzip + (magic numbers 1F 8B or 1F 9E), bzip2 (magic number 42 5A) and LZMA + (magic number 5D 00). The uncompressed payload is currently always ELF + (magic number 7F 45 4C 46). Field name: payload_length Type: read -- cgit v1.2.3-59-g8ed1b From e56d0cfe7790fd3218ae4f6aae1335547fea8763 Mon Sep 17 00:00:00 2001 From: Baodong Chen Date: Thu, 8 Jan 2009 19:24:29 +0800 Subject: Documentation/x86/boot.txt: modify fieldname Modify field names to the right ones: - start_sys was changed to start_sys_seg - iinitrd_addr_max was changed to ramdisk_max - pad2 was changed to pad2 and pad3 - readmode_swtch was changed to realmode_swtch Signed-off-by: Baodong Chen <[email]chenbdchenbd@gmail.com[email]> Acked-by: Jiri Kosina Signed-off-by: Ingo Molnar --- Documentation/x86/boot.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index 7b4596ac4120..12299697b7cd 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -158,7 +158,7 @@ Offset Proto Name Meaning 0202/4 2.00+ header Magic signature "HdrS" 0206/2 2.00+ version Boot protocol version supported 0208/4 2.00+ realmode_swtch Boot loader hook (see below) -020C/2 2.00+ start_sys The load-low segment (0x1000) (obsolete) +020C/2 2.00+ start_sys_seg The load-low segment (0x1000) (obsolete) 020E/2 2.00+ kernel_version Pointer to kernel version string 0210/1 2.00+ type_of_loader Boot loader identifier 0211/1 2.00+ loadflags Boot protocol option flags @@ -170,10 +170,11 @@ Offset Proto Name Meaning 0224/2 2.01+ heap_end_ptr Free memory after setup end 0226/2 N/A pad1 Unused 0228/4 2.02+ cmd_line_ptr 32-bit pointer to the kernel command line -022C/4 2.03+ initrd_addr_max Highest legal initrd address +022C/4 2.03+ ramdisk_max Highest legal initrd address 0230/4 2.05+ kernel_alignment Physical addr alignment required for kernel 0234/1 2.05+ relocatable_kernel Whether kernel is relocatable or not -0235/3 N/A pad2 Unused +0235/1 N/A pad2 Unused +0236/2 N/A pad3 Unused 0238/4 2.06+ cmdline_size Maximum size of the kernel command line 023C/4 2.07+ hardware_subarch Hardware subarchitecture 0240/8 2.07+ hardware_subarch_data Subarchitecture-specific data @@ -299,14 +300,14 @@ Protocol: 2.00+ e.g. 0x0204 for version 2.04, and 0x0a11 for a hypothetical version 10.17. -Field name: readmode_swtch +Field name: realmode_swtch Type: modify (optional) Offset/size: 0x208/4 Protocol: 2.00+ Boot loader hook (see ADVANCED BOOT LOADER HOOKS below.) -Field name: start_sys +Field name: start_sys_seg Type: read Offset/size: 0x20c/2 Protocol: 2.00+ @@ -468,7 +469,7 @@ Protocol: 2.02+ zero, the kernel will assume that your boot loader does not support the 2.02+ protocol. -Field name: initrd_addr_max +Field name: ramdisk_max Type: read Offset/size: 0x22c/4 Protocol: 2.03+ -- cgit v1.2.3-59-g8ed1b From 08989930f91e4802b94e03eb54e5385bac112811 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 21 Jan 2009 07:43:23 +0100 Subject: ALSA: hda - Remove old models for STAC9872 from the document Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 64eb1100eec1..75914bcdce72 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -352,5 +352,4 @@ STAC92HD83* STAC9872 ======== - vaio Setup for VAIO FE550G/SZ110 - vaio-ar Setup for VAIO AR + N/A -- cgit v1.2.3-59-g8ed1b From 792b48780e8b6435d017cef4b5c304876a48653e Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Fri, 16 Jan 2009 23:36:31 +0000 Subject: dccp: Implement both feature-local and feature-remote Sequence Window feature This adds full support for local/remote Sequence Window feature, from which the * sequence-number-validity (W) and * acknowledgment-number-validity (W') windows derive as specified in RFC 4340, 7.5.3. Specifically, the following is contained in this patch: * integrated new socket fields into dccp_sk; * updated the update_gsr/gss routines with regard to these fields; * updated handler code: the Sequence Window feature is located at the TX side, so the local feature is meant if the handler-rx flag is false; * the initialisation of `rcv_wnd' in reqsk is removed, since - rcv_wnd is not used by the code anywhere; - sequence number checks are not done in the LISTEN state (cf. 7.5.3); - dccp_check_req checks the Ack number validity more rigorously; * the `struct dccp_minisock' became empty and is now removed. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: David S. Miller --- Documentation/networking/dccp.txt | 3 ++- include/linux/dccp.h | 24 ++++-------------------- net/dccp/dccp.h | 16 +++++++--------- net/dccp/feat.c | 13 +++++++++++-- net/dccp/minisocks.c | 11 ----------- net/dccp/proto.c | 2 -- 6 files changed, 24 insertions(+), 45 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 7a3bb1abb830..b132e4a3cf0f 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -141,7 +141,8 @@ rx_ccid = 2 Default CCID for the receiver-sender half-connection; see tx_ccid. seq_window = 100 - The initial sequence window (sec. 7.5.2). + The initial sequence window (sec. 7.5.2) of the sender. This influences + the local ackno validity and the remote seqno validity windows (7.5.1). tx_qlen = 5 The size of the transmit buffer in packets. A value of 0 corresponds diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 990e97fa1f07..7a0502ab383a 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -363,19 +363,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) /* FIXME: for now we're default to 1 but it should really be 0 */ #define DCCPF_INITIAL_SEND_NDP_COUNT 1 -/** - * struct dccp_minisock - Minimal DCCP connection representation - * - * Will be used to pass the state from dccp_request_sock to dccp_sock. - * - * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) - */ -struct dccp_minisock { - __u64 dccpms_sequence_window; -}; - -extern void dccp_minisock_init(struct dccp_minisock *dmsk); - /** * struct dccp_request_sock - represent DCCP-specific connection request * @dreq_inet_rsk: structure inherited from @@ -464,13 +451,14 @@ struct dccp_ackvec; * @dccps_timestamp_time - time of receiving latest @dccps_timestamp_echo * @dccps_l_ack_ratio - feature-local Ack Ratio * @dccps_r_ack_ratio - feature-remote Ack Ratio + * @dccps_l_seq_win - local Sequence Window (influences ack number validity) + * @dccps_r_seq_win - remote Sequence Window (influences seq number validity) * @dccps_pcslen - sender partial checksum coverage (via sockopt) * @dccps_pcrlen - receiver partial checksum coverage (via sockopt) * @dccps_send_ndp_count - local Send NDP Count feature (7.7.2) * @dccps_ndp_count - number of Non Data Packets since last data packet * @dccps_mss_cache - current value of MSS (path MTU minus header sizes) * @dccps_rate_last - timestamp for rate-limiting DCCP-Sync (RFC 4340, 7.5.4) - * @dccps_minisock - associated minisock (accessed via dccp_msk) * @dccps_featneg - tracks feature-negotiation state (mostly during handshake) * @dccps_hc_rx_ackvec - rx half connection ack vector * @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection) @@ -504,12 +492,13 @@ struct dccp_sock { __u32 dccps_timestamp_time; __u16 dccps_l_ack_ratio; __u16 dccps_r_ack_ratio; + __u64 dccps_l_seq_win:48; + __u64 dccps_r_seq_win:48; __u8 dccps_pcslen:4; __u8 dccps_pcrlen:4; __u8 dccps_send_ndp_count:1; __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; - struct dccp_minisock dccps_minisock; struct list_head dccps_featneg; struct dccp_ackvec *dccps_hc_rx_ackvec; struct ccid *dccps_hc_rx_ccid; @@ -527,11 +516,6 @@ static inline struct dccp_sock *dccp_sk(const struct sock *sk) return (struct dccp_sock *)sk; } -static inline struct dccp_minisock *dccp_msk(const struct sock *sk) -{ - return (struct dccp_minisock *)&dccp_sk(sk)->dccps_minisock; -} - static inline const char *dccp_role(const struct sock *sk) { switch (dccp_sk(sk)->dccps_role) { diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index f2230fc168e1..04ae91898a68 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -409,23 +409,21 @@ static inline void dccp_hdr_set_ack(struct dccp_hdr_ack_bits *dhack, static inline void dccp_update_gsr(struct sock *sk, u64 seq) { struct dccp_sock *dp = dccp_sk(sk); - const struct dccp_minisock *dmsk = dccp_msk(sk); dp->dccps_gsr = seq; - dccp_set_seqno(&dp->dccps_swl, - dp->dccps_gsr + 1 - (dmsk->dccpms_sequence_window / 4)); - dccp_set_seqno(&dp->dccps_swh, - dp->dccps_gsr + (3 * dmsk->dccpms_sequence_window) / 4); + /* Sequence validity window depends on remote Sequence Window (7.5.1) */ + dp->dccps_swl = SUB48(ADD48(dp->dccps_gsr, 1), dp->dccps_r_seq_win / 4); + dp->dccps_swh = ADD48(dp->dccps_gsr, (3 * dp->dccps_r_seq_win) / 4); } static inline void dccp_update_gss(struct sock *sk, u64 seq) { struct dccp_sock *dp = dccp_sk(sk); - dp->dccps_awh = dp->dccps_gss = seq; - dccp_set_seqno(&dp->dccps_awl, - (dp->dccps_gss - - dccp_msk(sk)->dccpms_sequence_window + 1)); + dp->dccps_gss = seq; + /* Ack validity window depends on local Sequence Window value (7.5.1) */ + dp->dccps_awl = SUB48(ADD48(dp->dccps_gss, 1), dp->dccps_l_seq_win); + dp->dccps_awh = dp->dccps_gss; } static inline int dccp_ack_pending(const struct sock *sk) diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 67ffac9905f8..7303f79705d2 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -51,8 +51,17 @@ static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx) static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx) { - if (!rx) - dccp_msk(sk)->dccpms_sequence_window = seq_win; + struct dccp_sock *dp = dccp_sk(sk); + + if (rx) { + dp->dccps_r_seq_win = seq_win; + /* propagate changes to update SWL/SWH */ + dccp_update_gsr(sk, dp->dccps_gsr); + } else { + dp->dccps_l_seq_win = seq_win; + /* propagate changes to update AWL */ + dccp_update_gss(sk, dp->dccps_gss); + } return 0; } diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 6821ae33dd37..5ca49cec95f5 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -42,11 +42,6 @@ struct inet_timewait_death_row dccp_death_row = { EXPORT_SYMBOL_GPL(dccp_death_row); -void dccp_minisock_init(struct dccp_minisock *dmsk) -{ - dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; -} - void dccp_time_wait(struct sock *sk, int state, int timeo) { struct inet_timewait_sock *tw = NULL; @@ -110,7 +105,6 @@ struct sock *dccp_create_openreq_child(struct sock *sk, struct dccp_request_sock *dreq = dccp_rsk(req); struct inet_connection_sock *newicsk = inet_csk(newsk); struct dccp_sock *newdp = dccp_sk(newsk); - struct dccp_minisock *newdmsk = dccp_msk(newsk); newdp->dccps_role = DCCP_ROLE_SERVER; newdp->dccps_hc_rx_ackvec = NULL; @@ -128,10 +122,6 @@ struct sock *dccp_create_openreq_child(struct sock *sk, * Initialize S.GAR := S.ISS * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies */ - - /* See dccp_v4_conn_request */ - newdmsk->dccpms_sequence_window = req->rcv_wnd; - newdp->dccps_gar = newdp->dccps_iss = dreq->dreq_iss; dccp_update_gss(newsk, dreq->dreq_iss); @@ -290,7 +280,6 @@ int dccp_reqsk_init(struct request_sock *req, inet_rsk(req)->rmt_port = dccp_hdr(skb)->dccph_sport; inet_rsk(req)->loc_port = dccp_hdr(skb)->dccph_dport; inet_rsk(req)->acked = 0; - req->rcv_wnd = sysctl_dccp_feat_sequence_window; dreq->dreq_timestamp_echo = 0; /* inherit feature negotiation options from listening socket */ diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 945b4d5d23b3..314a1b5c033c 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -174,8 +174,6 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) struct dccp_sock *dp = dccp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); - dccp_minisock_init(&dp->dccps_minisock); - icsk->icsk_rto = DCCP_TIMEOUT_INIT; icsk->icsk_syn_retries = sysctl_dccp_request_retries; sk->sk_state = DCCP_CLOSED; -- cgit v1.2.3-59-g8ed1b From d9a4268ee92ba1a2355c892a3add1fa66856b510 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 22 Jan 2009 17:40:18 +0100 Subject: ALSA: hda - Add quirk for Gateway %1616 laptop Gateway T1616 laptop needs EAPD always on while the current STAC9205 code turns off per HP plug. Added a new model "eapd" to keep it on. Reference: Novell bnc#467597 https://bugzilla.novell.com/show_bug.cgi?id=467597 Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 1 + sound/pci/hda/patch_sigmatel.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 75914bcdce72..ef6b22e25412 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -285,6 +285,7 @@ STAC9205/9254 dell-m42 Dell (unknown) dell-m43 Dell Precision dell-m44 Dell Inspiron + eapd Keep EAPD on (e.g. Gateway T1616) STAC9220/9221 ============= diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3f85731055c0..ed2fa431b03f 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -66,6 +66,7 @@ enum { STAC_9205_DELL_M42, STAC_9205_DELL_M43, STAC_9205_DELL_M44, + STAC_9205_EAPD, STAC_9205_MODELS }; @@ -2240,6 +2241,7 @@ static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs, [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs, [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs, + [STAC_9205_EAPD] = NULL, }; static const char *stac9205_models[STAC_9205_MODELS] = { @@ -2247,12 +2249,14 @@ static const char *stac9205_models[STAC_9205_MODELS] = { [STAC_9205_DELL_M42] = "dell-m42", [STAC_9205_DELL_M43] = "dell-m43", [STAC_9205_DELL_M44] = "dell-m44", + [STAC_9205_EAPD] = "eapd", }; static struct snd_pci_quirk stac9205_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_9205_REF), + /* Dell */ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, "unknown Dell", STAC_9205_DELL_M42), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2, @@ -2283,6 +2287,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = { "Dell Inspiron", STAC_9205_DELL_M44), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, "Dell Vostro 1500", STAC_9205_DELL_M42), + /* Gateway */ + SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD), {} /* terminator */ }; @@ -5320,7 +5326,9 @@ static int patch_stac9205(struct hda_codec *codec) spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; - spec->eapd_switch = 1; + /* Turn on/off EAPD per HP plugging */ + if (spec->board_config != STAC_9205_EAPD) + spec->eapd_switch = 1; spec->multiout.dac_nids = spec->dac_nids; switch (spec->board_config){ -- cgit v1.2.3-59-g8ed1b From c96330b083ce88b9fea428df99b4631f1b6410ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 28 Jan 2009 08:23:03 +0100 Subject: ALSA: Add description of new snd-msnd-* drivers Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/ALSA-Configuration.txt | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 841a9365d5fd..ba7b14a13ab7 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -1185,6 +1185,54 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. This module supports multiple devices and PnP. + Module snd-msnd-classic + ----------------------- + + Module for Turtle Beach MultiSound Classic, Tahiti or Monterey + soundcards. + + io - Port # for msnd-classic card + irq - IRQ # for msnd-classic card + mem - Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000, + 0xe0000 or 0xe8000) + write_ndelay - enable write ndelay (default = 1) + calibrate_signal - calibrate signal (default = 0) + isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) + digital - Digital daughterboard present (default = 0) + cfg - Config port (0x250, 0x260 or 0x270) default = PnP + reset - Reset all devices + mpu_io - MPU401 I/O port + mpu_irq - MPU401 irq# + ide_io0 - IDE port #0 + ide_io1 - IDE port #1 + ide_irq - IDE irq# + joystick_io - Joystick I/O port + + The driver requires firmware files "turtlebeach/msndinit.bin" and + "turtlebeach/msndperm.bin" in the proper firmware directory. + + See Documentation/sound/oss/MultiSound for important information + about this driver. Note that it has been discontinued, but the + Voyetra Turtle Beach knowledge base entry for it is still available + at + http://www.turtlebeach.com/site/kb_ftp/790.asp + + Module snd-msnd-pinnacle + ------------------------ + + Module for Turtle Beach MultiSound Pinnacle/Fiji soundcards. + + io - Port # for pinnacle/fiji card + irq - IRQ # for pinnalce/fiji card + mem - Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000, + 0xe0000 or 0xe8000) + write_ndelay - enable write ndelay (default = 1) + calibrate_signal - calibrate signal (default = 0) + isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) + + The driver requires firmware files "turtlebeach/pndspini.bin" and + "turtlebeach/pndsperm.bin" in the proper firmware directory. + Module snd-mtpav ---------------- -- cgit v1.2.3-59-g8ed1b From b4f7ec46b6c151d31c068e46278efef7e43b5043 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 14 Jan 2009 15:52:41 +0100 Subject: powerpc: convert dts-bindings/fsl/dma.txt to dts-v1 syntax Signed-off-by: Peter Korsgaard Signed-off-by: Kumar Gala --- Documentation/powerpc/dts-bindings/fsl/dma.txt | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/dts-bindings/fsl/dma.txt b/Documentation/powerpc/dts-bindings/fsl/dma.txt index cc453110fc46..0732cdd05ba1 100644 --- a/Documentation/powerpc/dts-bindings/fsl/dma.txt +++ b/Documentation/powerpc/dts-bindings/fsl/dma.txt @@ -35,30 +35,30 @@ Example: #address-cells = <1>; #size-cells = <1>; compatible = "fsl,mpc8349-dma", "fsl,elo-dma"; - reg = <82a8 4>; - ranges = <0 8100 1a4>; + reg = <0x82a8 4>; + ranges = <0 0x8100 0x1a4>; interrupt-parent = <&ipic>; - interrupts = <47 8>; + interrupts = <71 8>; cell-index = <0>; dma-channel@0 { compatible = "fsl,mpc8349-dma-channel", "fsl,elo-dma-channel"; cell-index = <0>; - reg = <0 80>; + reg = <0 0x80>; }; dma-channel@80 { compatible = "fsl,mpc8349-dma-channel", "fsl,elo-dma-channel"; cell-index = <1>; - reg = <80 80>; + reg = <0x80 0x80>; }; dma-channel@100 { compatible = "fsl,mpc8349-dma-channel", "fsl,elo-dma-channel"; cell-index = <2>; - reg = <100 80>; + reg = <0x100 0x80>; }; dma-channel@180 { compatible = "fsl,mpc8349-dma-channel", "fsl,elo-dma-channel"; cell-index = <3>; - reg = <180 80>; + reg = <0x180 0x80>; }; }; @@ -93,36 +93,36 @@ Example: #address-cells = <1>; #size-cells = <1>; compatible = "fsl,mpc8540-dma", "fsl,eloplus-dma"; - reg = <21300 4>; - ranges = <0 21100 200>; + reg = <0x21300 4>; + ranges = <0 0x21100 0x200>; cell-index = <0>; dma-channel@0 { compatible = "fsl,mpc8540-dma-channel", "fsl,eloplus-dma-channel"; - reg = <0 80>; + reg = <0 0x80>; cell-index = <0>; interrupt-parent = <&mpic>; - interrupts = <14 2>; + interrupts = <20 2>; }; dma-channel@80 { compatible = "fsl,mpc8540-dma-channel", "fsl,eloplus-dma-channel"; - reg = <80 80>; + reg = <0x80 0x80>; cell-index = <1>; interrupt-parent = <&mpic>; - interrupts = <15 2>; + interrupts = <21 2>; }; dma-channel@100 { compatible = "fsl,mpc8540-dma-channel", "fsl,eloplus-dma-channel"; - reg = <100 80>; + reg = <0x100 0x80>; cell-index = <2>; interrupt-parent = <&mpic>; - interrupts = <16 2>; + interrupts = <22 2>; }; dma-channel@180 { compatible = "fsl,mpc8540-dma-channel", "fsl,eloplus-dma-channel"; - reg = <180 80>; + reg = <0x180 0x80>; cell-index = <3>; interrupt-parent = <&mpic>; - interrupts = <17 2>; + interrupts = <23 2>; }; }; -- cgit v1.2.3-59-g8ed1b From 9e128fddcc589db4e7d9e8328f656ae4a21a2808 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 29 Jan 2009 11:49:10 +0100 Subject: ALSA: Add missing description of snd-cmi8330 module parameters Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/ALSA-Configuration.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 841a9365d5fd..7134a8f70442 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -346,6 +346,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. sbirq - IRQ # for CMI8330 chip (SB16) sbdma8 - 8bit DMA # for CMI8330 chip (SB16) sbdma16 - 16bit DMA # for CMI8330 chip (SB16) + fmport - (optional) OPL3 I/O port + mpuport - (optional) MPU401 I/O port + mpuirq - (optional) MPU401 irq # This module supports multiple cards and autoprobe. -- cgit v1.2.3-59-g8ed1b From c557289cb8ea063bd09db88f8a687a841556e291 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sat, 27 Dec 2008 18:26:39 +0100 Subject: b43: Change schedule for old-fw support removal The scheduled date for the removal of old fw support was in July 2008. However, we're not going to remove the support unless it causes a major headache. So change the schedule from "July 2008" to "when it causes headaches". Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- Documentation/feature-removal-schedule.txt | 4 +++- drivers/net/wireless/b43/main.c | 10 +++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 5ddbe350487a..ac98851f7a0c 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -229,7 +229,9 @@ Who: Jan Engelhardt --------------------------- What: b43 support for firmware revision < 410 -When: July 2008 +When: The schedule was July 2008, but it was decided that we are going to keep the + code as long as there are no major maintanance headaches. + So it _could_ be removed _any_ time now, if it conflicts with something new. Why: The support code for the old firmware hurts code readability/maintainability and slightly hurts runtime performance. Bugfixes for the old firmware are not provided by Broadcom anymore. diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index c627bac87a40..5ca55dcd0345 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -1954,8 +1954,9 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error) const char *text; text = "You must go to " - "http://linuxwireless.org/en/users/Drivers/b43#devicefirmware " - "and download the latest firmware (version 4).\n"; + "http://wireless.kernel.org/en/users/Drivers/b43#devicefirmware " + "and download the correct firmware for this driver version. " + "Please carefully read all instructions on this website.\n"; if (error) b43err(wl, text); else @@ -2271,8 +2272,11 @@ static int b43_upload_microcode(struct b43_wldev *dev) } if (b43_is_old_txhdr_format(dev)) { + /* We're over the deadline, but we keep support for old fw + * until it turns out to be in major conflict with something new. */ b43warn(dev->wl, "You are using an old firmware image. " - "Support for old firmware will be removed in July 2008.\n"); + "Support for old firmware will be removed soon " + "(official deadline was July 2008).\n"); b43_print_fw_helptext(dev->wl, 0); } -- cgit v1.2.3-59-g8ed1b From d1c3a37ceeb1a5ea02991a0476355f1a1d3b3e83 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Jan 2009 00:26:10 +0100 Subject: mac80211: clarify alignment docs, fix up alignment Not all drivers are capable of passing properly aligned frames, in particular with mesh networking no hardware will support completely aligning it correctly. This patch adds code to align the data payload to a 4-byte boundary in memory for those platforms that require this, or when CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT is set. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/mac80211.tmpl | 4 +- net/mac80211/rx.c | 110 +++++++++++++++++++++++++----------- 2 files changed, 79 insertions(+), 35 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl index 77c3c202991b..bdf908a6e545 100644 --- a/Documentation/DocBook/mac80211.tmpl +++ b/Documentation/DocBook/mac80211.tmpl @@ -165,8 +165,8 @@ usage should require reading the full document. !Pinclude/net/mac80211.h Frame format - Alignment issues - TBD + Packet alignment +!Pnet/mac80211/rx.c Packet alignment Calling into mac80211 from interrupts diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index ddb966f58882..b68e082e99ce 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -102,7 +102,7 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, return len; } -/** +/* * ieee80211_add_rx_radiotap_header - add radiotap header * * add a radiotap header containing all the fields which the hardware provided. @@ -371,39 +371,50 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) rx->skb->priority = (tid > 7) ? 0 : tid; } -static void ieee80211_verify_ip_alignment(struct ieee80211_rx_data *rx) +/** + * DOC: Packet alignment + * + * Drivers always need to pass packets that are aligned to two-byte boundaries + * to the stack. + * + * Additionally, should, if possible, align the payload data in a way that + * guarantees that the contained IP header is aligned to a four-byte + * boundary. In the case of regular frames, this simply means aligning the + * payload to a four-byte boundary (because either the IP header is directly + * contained, or IV/RFC1042 headers that have a length divisible by four are + * in front of it). + * + * With A-MSDU frames, however, the payload data address must yield two modulo + * four because there are 14-byte 802.3 headers within the A-MSDU frames that + * push the IP header further back to a multiple of four again. Thankfully, the + * specs were sane enough this time around to require padding each A-MSDU + * subframe to a length that is a multiple of four. + * + * Padding like Atheros hardware adds which is inbetween the 802.11 header and + * the payload is not supported, the driver is required to move the 802.11 + * header to be directly in front of the payload in that case. + */ +static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx) { -#ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; int hdrlen; +#ifndef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT + return; +#endif + + if (WARN_ONCE((unsigned long)rx->skb->data & 1, + "unaligned packet at 0x%p\n", rx->skb->data)) + return; + if (!ieee80211_is_data_present(hdr->frame_control)) return; - /* - * Drivers are required to align the payload data in a way that - * guarantees that the contained IP header is aligned to a four- - * byte boundary. In the case of regular frames, this simply means - * aligning the payload to a four-byte boundary (because either - * the IP header is directly contained, or IV/RFC1042 headers that - * have a length divisible by four are in front of it. - * - * With A-MSDU frames, however, the payload data address must - * yield two modulo four because there are 14-byte 802.3 headers - * within the A-MSDU frames that push the IP header further back - * to a multiple of four again. Thankfully, the specs were sane - * enough this time around to require padding each A-MSDU subframe - * to a length that is a multiple of four. - * - * Padding like atheros hardware adds which is inbetween the 802.11 - * header and the payload is not supported, the driver is required - * to move the 802.11 header further back in that case. - */ hdrlen = ieee80211_hdrlen(hdr->frame_control); if (rx->flags & IEEE80211_RX_AMSDU) hdrlen += ETH_HLEN; - WARN_ON_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3); -#endif + WARN_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3, + "unaligned IP payload at 0x%p\n", rx->skb->data + hdrlen); } @@ -1267,10 +1278,37 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) } if (skb) { - /* deliver to local stack */ - skb->protocol = eth_type_trans(skb, dev); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx(skb); + int align __maybe_unused; + +#if defined(CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT) || !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + /* + * 'align' will only take the values 0 or 2 here + * since all frames are required to be aligned + * to 2-byte boundaries when being passed to + * mac80211. That also explains the __skb_push() + * below. + */ + align = (unsigned long)skb->data & 4; + if (align) { + if (WARN_ON(skb_headroom(skb) < 3)) { + dev_kfree_skb(skb); + skb = NULL; + } else { + u8 *data = skb->data; + size_t len = skb->len; + u8 *new = __skb_push(skb, align); + memmove(new, data, len); + __skb_trim(skb, len); + } + } +#endif + + if (skb) { + /* deliver to local stack */ + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); + } } if (xmit_skb) { @@ -1339,14 +1377,20 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) if (remaining <= subframe_len + padding) frame = skb; else { - frame = dev_alloc_skb(local->hw.extra_tx_headroom + - subframe_len); + /* + * Allocate and reserve two bytes more for payload + * alignment since sizeof(struct ethhdr) is 14. + */ + frame = dev_alloc_skb( + ALIGN(local->hw.extra_tx_headroom, 4) + + subframe_len + 2); if (frame == NULL) return RX_DROP_UNUSABLE; - skb_reserve(frame, local->hw.extra_tx_headroom + - sizeof(struct ethhdr)); + skb_reserve(frame, + ALIGN(local->hw.extra_tx_headroom, 4) + + sizeof(struct ethhdr) + 2); memcpy(skb_put(frame, ntohs(len)), skb->data, ntohs(len)); @@ -1976,7 +2020,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, rx.flags |= IEEE80211_RX_IN_SCAN; ieee80211_parse_qos(&rx); - ieee80211_verify_ip_alignment(&rx); + ieee80211_verify_alignment(&rx); skb = rx.skb; -- cgit v1.2.3-59-g8ed1b From 4be8c3873e0b88397866d3ede578503e188f9ad2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Jan 2009 18:28:20 +0100 Subject: mac80211: extend/document powersave API This modifies hardware flags for powersave to support three different flags: * IEEE80211_HW_SUPPORTS_PS - indicates general PS support * IEEE80211_HW_PS_NULLFUNC_STACK - indicates nullfunc sending in software * IEEE80211_HW_SUPPORTS_DYNAMIC_PS - indicates dynamic PS on the device It also adds documentation for all this which explains how to set the various flags. Additionally, it fixes a few things: * a spot where && was used to test flags * enable CONF_PS only when associated again Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/mac80211.tmpl | 8 +++-- drivers/net/wireless/iwlwifi/iwl-core.c | 3 +- drivers/net/wireless/rt2x00/rt2400pci.c | 4 ++- drivers/net/wireless/rt2x00/rt2500pci.c | 4 ++- drivers/net/wireless/rt2x00/rt2500usb.c | 4 ++- drivers/net/wireless/rt2x00/rt61pci.c | 4 ++- drivers/net/wireless/rt2x00/rt73usb.c | 4 ++- include/net/mac80211.h | 53 +++++++++++++++++++++++++++++---- net/mac80211/mlme.c | 31 ++++++++++--------- net/mac80211/tx.c | 2 +- net/mac80211/wext.c | 40 +++++++++++++++---------- 11 files changed, 111 insertions(+), 46 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl index bdf908a6e545..8af6d9626878 100644 --- a/Documentation/DocBook/mac80211.tmpl +++ b/Documentation/DocBook/mac80211.tmpl @@ -17,8 +17,7 @@ - 2007 - 2008 + 2007-2009 Johannes Berg @@ -223,6 +222,11 @@ usage should require reading the full document. !Finclude/net/mac80211.h ieee80211_key_flags + + Powersave support +!Pinclude/net/mac80211.h Powersave support + + Multiple queues and QoS support TBD diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 76315c30e6fc..07c3870365be 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -805,7 +805,8 @@ int iwl_setup_mac(struct iwl_priv *priv) /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | - IEEE80211_HW_AMPDU_AGGREGATION; + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SUPPORTS_PS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 9104113270d0..ae8bfd6b59df 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1449,7 +1449,9 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) * Initialize all hw fields. */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = 0; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index ebcc49770922..bca6798be153 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1749,7 +1749,9 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) * Initialize all hw fields. */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = 0; diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index e992bad64644..27a6971df579 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1801,7 +1801,9 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 82d35a5a4aa7..549480a963e9 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2556,7 +2556,9 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = 0; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 2b70c01b55e9..849220236c7d 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2077,7 +2077,9 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) */ rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 83ee8a212296..8a305bfdb87b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -850,10 +850,15 @@ enum ieee80211_tkip_key_type { * @IEEE80211_HW_AMPDU_AGGREGATION: * Hardware supports 11n A-MPDU aggregation. * - * @IEEE80211_HW_NO_STACK_DYNAMIC_PS: - * Hardware which has dynamic power save support, meaning - * that power save is enabled in idle periods, and don't need support - * from stack. + * @IEEE80211_HW_SUPPORTS_PS: + * Hardware has power save support (i.e. can go to sleep). + * + * @IEEE80211_HW_PS_NULLFUNC_STACK: + * Hardware requires nullfunc frame handling in stack, implies + * stack support for dynamic PS. + * + * @IEEE80211_HW_SUPPORTS_DYNAMIC_PS: + * Hardware has support for dynamic PS. */ enum ieee80211_hw_flags { IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, @@ -866,7 +871,9 @@ enum ieee80211_hw_flags { IEEE80211_HW_NOISE_DBM = 1<<8, IEEE80211_HW_SPECTRUM_MGMT = 1<<9, IEEE80211_HW_AMPDU_AGGREGATION = 1<<10, - IEEE80211_HW_NO_STACK_DYNAMIC_PS = 1<<11, + IEEE80211_HW_SUPPORTS_PS = 1<<11, + IEEE80211_HW_PS_NULLFUNC_STACK = 1<<12, + IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<13, }; /** @@ -1052,6 +1059,42 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * handler is software decryption with wrap around of iv16. */ +/** + * DOC: Powersave support + * + * mac80211 has support for various powersave implementations. + * + * First, it can support hardware that handles all powersaving by + * itself, such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS + * hardware flag. In that case, it will be told about the desired + * powersave mode depending on the association status, and the driver + * must take care of sending nullfunc frames when necessary, i.e. when + * entering and leaving powersave mode. The driver is required to look at + * the AID in beacons and signal to the AP that it woke up when it finds + * traffic directed to it. This mode supports dynamic PS by simply + * enabling/disabling PS. + * + * Additionally, such hardware may set the %IEEE80211_HW_SUPPORTS_DYNAMIC_PS + * flag to indicate that it can support dynamic PS mode itself (see below). + * + * Other hardware designs cannot send nullfunc frames by themselves and also + * need software support for parsing the TIM bitmap. This is also supported + * by mac80211 by combining the %IEEE80211_HW_SUPPORTS_PS and + * %IEEE80211_HW_PS_NULLFUNC_STACK flags. The hardware is of course still + * required to pass up beacons. Additionally, in this case, mac80211 will + * wake up the hardware when multicast traffic is announced in the beacon. + * + * FIXME: I don't think we can be fast enough in software when we want to + * receive multicast traffic? + * + * Dynamic powersave mode is an extension to normal powersave mode in which + * the hardware stays awake for a user-specified period of time after sending + * a frame so that reply frames need not be buffered and therefore delayed + * to the next wakeup. This can either be supported by hardware, in which case + * the driver needs to look at the @dynamic_ps_timeout hardware configuration + * value, or by the stack if all nullfunc handling is in the stack. + */ + /** * DOC: Frame filtering * diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7709e7645671..a1e683e305f0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -775,17 +775,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= BSS_CHANGED_BASIC_RATES; ieee80211_bss_info_change_notify(sdata, bss_info_changed); - if (local->powersave && - !(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) { - if (local->hw.conf.dynamic_ps_timeout > 0) + if (local->powersave) { + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) && + local->hw.conf.dynamic_ps_timeout > 0) { mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies( local->hw.conf.dynamic_ps_timeout)); - else { - ieee80211_send_nullfunc(local, sdata, 1); + } else { + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) + ieee80211_send_nullfunc(local, sdata, 1); conf->flags |= IEEE80211_CONF_PS; - ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } } @@ -1779,16 +1779,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param, elems.wmm_param_len); - if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) { + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK && + local->hw.conf.flags & IEEE80211_CONF_PS) { directed_tim = check_tim(&elems, ifsta->aid, &is_mc); if (directed_tim || is_mc) { - if (local->hw.conf.flags && IEEE80211_CONF_PS) { - local->hw.conf.flags &= ~IEEE80211_CONF_PS; - ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); - ieee80211_send_nullfunc(local, sdata, 0); - } + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + ieee80211_send_nullfunc(local, sdata, 0); } } @@ -2694,9 +2692,10 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) if (local->hw.conf.flags & IEEE80211_CONF_PS) return; - ieee80211_send_nullfunc(local, sdata, 1); - local->hw.conf.flags |= IEEE80211_CONF_PS; + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) + ieee80211_send_nullfunc(local, sdata, 1); + local->hw.conf.flags |= IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b18a72690119..cd6bc87eec73 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1295,7 +1295,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } - if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) && + if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && local->hw.conf.dynamic_ps_timeout > 0) { if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 3f2db0bda46c..1e5b29bdb3a7 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -837,6 +837,9 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, int ret = 0, timeout = 0; bool ps; + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) + return -EOPNOTSUPP; + if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EINVAL; @@ -862,32 +865,37 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev, if (wrq->flags & IW_POWER_TIMEOUT) timeout = wrq->value / 1000; -set: + set: if (ps == local->powersave && timeout == conf->dynamic_ps_timeout) return ret; local->powersave = ps; conf->dynamic_ps_timeout = timeout; - if (local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) { + if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT); - } else if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { - if (conf->dynamic_ps_timeout > 0) - mod_timer(&local->dynamic_ps_timer, jiffies + - msecs_to_jiffies(conf->dynamic_ps_timeout)); - else { - if (local->powersave) { + + if (!(sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)) + return ret; + + if (conf->dynamic_ps_timeout > 0 && + !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) { + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies(conf->dynamic_ps_timeout)); + } else { + if (local->powersave) { + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) ieee80211_send_nullfunc(local, sdata, 1); - conf->flags |= IEEE80211_CONF_PS; - ret = ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); - } else { - conf->flags &= ~IEEE80211_CONF_PS; - ret = ieee80211_hw_config(local, - IEEE80211_CONF_CHANGE_PS); + conf->flags |= IEEE80211_CONF_PS; + ret = ieee80211_hw_config(local, + IEEE80211_CONF_CHANGE_PS); + } else { + conf->flags &= ~IEEE80211_CONF_PS; + ret = ieee80211_hw_config(local, + IEEE80211_CONF_CHANGE_PS); + if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) ieee80211_send_nullfunc(local, sdata, 0); - } } } -- cgit v1.2.3-59-g8ed1b From 504a06d8b05cb5b214c9b97752d8451e88d9ef81 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 30 Jan 2009 19:59:10 +0100 Subject: ALSA: Add description of new fm_port option for snd-es1688 driver Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/ALSA-Configuration.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 7134a8f70442..a763b76afe51 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -609,6 +609,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. Module for ESS AudioDrive ES-1688 and ES-688 sound cards. port - port # for ES-1688 chip (0x220,0x240,0x260) + fm_port - port # for OPL3 (option; share the same port as default) mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default) irq - IRQ # for ES-1688 chip (5,7,9,10) mpu_irq - IRQ # for MPU-401 port (5,7,9,10) -- cgit v1.2.3-59-g8ed1b From eefef1cf7653cd4e0aaf743c00ae8345086cdc01 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 1 Feb 2009 01:04:33 -0800 Subject: net: add ARP notify option for devices This adds another inet device option to enable gratuitous ARP when device is brought up or address change. This is handy for clusters or virtualization. Signed-off-by: Stephen Hemminger Signed-off-by: Jeremy Fitzhardinge Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 6 ++++++ include/linux/inetdevice.h | 1 + include/linux/sysctl.h | 1 + kernel/sysctl_check.c | 1 + net/ipv4/devinet.c | 9 +++++++++ 5 files changed, 18 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index c7712787933c..ff3f219ee4d7 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -782,6 +782,12 @@ arp_ignore - INTEGER The max value from conf/{all,interface}/arp_ignore is used when ARP request is received on the {interface} +arp_notify - BOOLEAN + Define mode for notification of address and device changes. + 0 - (default): do nothing + 1 - Generate gratuitous arp replies when device is brought up + or hardware address changes. + arp_accept - BOOLEAN Define behavior when gratuitous arp replies are received: 0 - drop gratuitous arp frames diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 06fcdb45106b..acef2a770b6b 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -108,6 +108,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) #define IN_DEV_ARPFILTER(in_dev) IN_DEV_ORCONF((in_dev), ARPFILTER) #define IN_DEV_ARP_ANNOUNCE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_ANNOUNCE) #define IN_DEV_ARP_IGNORE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_IGNORE) +#define IN_DEV_ARP_NOTIFY(in_dev) IN_DEV_MAXCONF((in_dev), ARP_NOTIFY) struct in_ifaddr { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 39d471d1163b..e76d3b22a466 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -490,6 +490,7 @@ enum NET_IPV4_CONF_ARP_IGNORE=19, NET_IPV4_CONF_PROMOTE_SECONDARIES=20, NET_IPV4_CONF_ARP_ACCEPT=21, + NET_IPV4_CONF_ARP_NOTIFY=22, __NET_IPV4_CONF_MAX }; diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c index fafeb48f27c0..b38423ca711a 100644 --- a/kernel/sysctl_check.c +++ b/kernel/sysctl_check.c @@ -219,6 +219,7 @@ static const struct trans_ctl_table trans_net_ipv4_conf_vars_table[] = { { NET_IPV4_CONF_ARP_IGNORE, "arp_ignore" }, { NET_IPV4_CONF_PROMOTE_SECONDARIES, "promote_secondaries" }, { NET_IPV4_CONF_ARP_ACCEPT, "arp_accept" }, + { NET_IPV4_CONF_ARP_NOTIFY, "arp_notify" }, {} }; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 309997edc8a5..d519a6a66726 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1075,6 +1075,14 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, } } ip_mc_up(in_dev); + /* fall through */ + case NETDEV_CHANGEADDR: + if (IN_DEV_ARP_NOTIFY(in_dev)) + arp_send(ARPOP_REQUEST, ETH_P_ARP, + in_dev->ifa_list->ifa_address, + dev, + in_dev->ifa_list->ifa_address, + NULL, dev->dev_addr, NULL); break; case NETDEV_DOWN: ip_mc_down(in_dev); @@ -1439,6 +1447,7 @@ static struct devinet_sysctl_table { DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), + DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), -- cgit v1.2.3-59-g8ed1b From 123848e77623b9996288e85433985439c157fcd0 Mon Sep 17 00:00:00 2001 From: Tony Vroon Date: Tue, 3 Feb 2009 11:13:34 +0000 Subject: ALSA: Document tyan model for Realtek ALC262 As just pointed out to me, the new tyan model for ALC262 was implemented but not documented. This adds the board to the list, using both its marketing name (Thunder n6650W) and its model number (S2915-E). Signed-off-by: Tony Vroon Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index c9df9db5835a..8f40999a456e 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -56,6 +56,7 @@ ALC262 sony-assamd Sony ASSAMD toshiba-s06 Toshiba S06 toshiba-rx1 Toshiba RX1 + tyan Tyan Thunder n6650W (S2915-E) ultra Samsung Q1 Ultra Vista model lenovo-3000 Lenovo 3000 y410 nec NEC Versa S9100 -- cgit v1.2.3-59-g8ed1b From 4d7902f22b0804730b80f7a4147f676430248a3a Mon Sep 17 00:00:00 2001 From: Andy Fleming Date: Wed, 4 Feb 2009 16:43:44 -0800 Subject: gianfar: Fix stashing support Stashing is only supported on the 85xx (e500-based) SoCs. The 83xx and 86xx chips don't have a proper cache for this. U-Boot has been updated to add stashing properties to the device tree nodes of gianfar devices on 85xx. So now we modify Linux to keep stashing off unless those properties are there. Signed-off-by: Andy Fleming Signed-off-by: David S. Miller --- Documentation/powerpc/dts-bindings/fsl/tsec.txt | 6 ++++++ drivers/net/gianfar.c | 23 +++++++++++++++++++++++ drivers/net/gianfar_sysfs.c | 12 +++++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/dts-bindings/fsl/tsec.txt b/Documentation/powerpc/dts-bindings/fsl/tsec.txt index 7fa4b27574b5..edb7ae19e868 100644 --- a/Documentation/powerpc/dts-bindings/fsl/tsec.txt +++ b/Documentation/powerpc/dts-bindings/fsl/tsec.txt @@ -56,6 +56,12 @@ Properties: hardware. - fsl,magic-packet : If present, indicates that the hardware supports waking up via magic packet. + - bd-stash : If present, indicates that the hardware supports stashing + buffer descriptors in the L2. + - rx-stash-len : Denotes the number of bytes of a received buffer to stash + in the L2. + - rx-stash-idx : Denotes the index of the first byte from the received + buffer to stash in the L2. Example: ethernet@24000 { diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 33de25602b32..dadd08cd801b 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -164,6 +164,9 @@ static int gfar_of_init(struct net_device *dev) struct gfar_private *priv = netdev_priv(dev); struct device_node *np = priv->node; char bus_name[MII_BUS_ID_SIZE]; + const u32 *stash; + const u32 *stash_len; + const u32 *stash_idx; if (!np || !of_device_is_available(np)) return -ENODEV; @@ -193,6 +196,26 @@ static int gfar_of_init(struct net_device *dev) } } + stash = of_get_property(np, "bd-stash", NULL); + + if(stash) { + priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING; + priv->bd_stash_en = 1; + } + + stash_len = of_get_property(np, "rx-stash-len", NULL); + + if (stash_len) + priv->rx_stash_size = *stash_len; + + stash_idx = of_get_property(np, "rx-stash-idx", NULL); + + if (stash_idx) + priv->rx_stash_index = *stash_idx; + + if (stash_len || stash_idx) + priv->device_flags |= FSL_GIANFAR_DEV_HAS_BUF_STASHING; + mac_addr = of_get_mac_address(np); if (mac_addr) memcpy(dev->dev_addr, mac_addr, MAC_ADDR_LEN); diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c index 74e0b4d42587..dd26da74f27a 100644 --- a/drivers/net/gianfar_sysfs.c +++ b/drivers/net/gianfar_sysfs.c @@ -53,6 +53,9 @@ static ssize_t gfar_set_bd_stash(struct device *dev, u32 temp; unsigned long flags; + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BD_STASHING)) + return count; + /* Find out the new setting */ if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1)) new_setting = 1; @@ -100,6 +103,9 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev, u32 temp; unsigned long flags; + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING)) + return count; + spin_lock_irqsave(&priv->rxlock, flags); if (length > priv->rx_buffer_size) goto out; @@ -152,6 +158,9 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev, u32 temp; unsigned long flags; + if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING)) + return count; + spin_lock_irqsave(&priv->rxlock, flags); if (index > priv->rx_stash_size) goto out; @@ -294,12 +303,9 @@ void gfar_init_sysfs(struct net_device *dev) int rc; /* Initialize the default values */ - priv->rx_stash_size = DEFAULT_STASH_LENGTH; - priv->rx_stash_index = DEFAULT_STASH_INDEX; priv->fifo_threshold = DEFAULT_FIFO_TX_THR; priv->fifo_starve = DEFAULT_FIFO_TX_STARVE; priv->fifo_starve_off = DEFAULT_FIFO_TX_STARVE_OFF; - priv->bd_stash_en = DEFAULT_BD_STASH; /* Create our sysfs files */ rc = device_create_file(&dev->dev, &dev_attr_bd_stash); -- cgit v1.2.3-59-g8ed1b From 6146f0d5e47ca4047ffded0fb79b6c25359b386c Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 4 Feb 2009 09:06:57 -0500 Subject: integrity: IMA hooks This patch replaces the generic integrity hooks, for which IMA registered itself, with IMA integrity hooks in the appropriate places directly in the fs directory. Signed-off-by: Mimi Zohar Acked-by: Serge Hallyn Signed-off-by: James Morris --- Documentation/kernel-parameters.txt | 1 + fs/exec.c | 10 +++++++++ fs/file_table.c | 2 ++ fs/inode.c | 24 ++++++++++++++------ fs/namei.c | 8 +++++++ include/linux/ima.h | 44 +++++++++++++++++++++++++++++++++++++ mm/mmap.c | 4 ++++ 7 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 include/linux/ima.h (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index a2d8805c03d5..7c67b94d1823 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -44,6 +44,7 @@ parameter is applicable: FB The frame buffer device is enabled. HW Appropriate hardware is enabled. IA-64 IA-64 architecture is enabled. + IMA Integrity measurement architecture is enabled. IOSCHED More than one I/O scheduler is enabled. IP_PNP IP DHCP, BOOTP, or RARP is enabled. ISAPNP ISA PnP code is enabled. diff --git a/fs/exec.c b/fs/exec.c index 02d2e120542d..9c789a525cc4 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -128,6 +129,9 @@ asmlinkage long sys_uselib(const char __user * library) goto exit; error = vfs_permission(&nd, MAY_READ | MAY_EXEC | MAY_OPEN); + if (error) + goto exit; + error = ima_path_check(&nd.path, MAY_READ | MAY_EXEC | MAY_OPEN); if (error) goto exit; @@ -681,6 +685,9 @@ struct file *open_exec(const char *name) goto out_path_put; err = vfs_permission(&nd, MAY_EXEC | MAY_OPEN); + if (err) + goto out_path_put; + err = ima_path_check(&nd.path, MAY_EXEC | MAY_OPEN); if (err) goto out_path_put; @@ -1207,6 +1214,9 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) } #endif retval = security_bprm_check(bprm); + if (retval) + return retval; + retval = ima_bprm_check(bprm); if (retval) return retval; diff --git a/fs/file_table.c b/fs/file_table.c index 0fbcacc3ea75..55895ccc08c6 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -276,6 +277,7 @@ void __fput(struct file *file) if (file->f_op && file->f_op->release) file->f_op->release(inode, file); security_file_free(file); + ima_file_free(file); if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL)) cdev_put(inode->i_cdev); fops_put(file->f_op); diff --git a/fs/inode.c b/fs/inode.c index 098a2443196f..ed22b14f2202 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -144,13 +145,13 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode) inode->i_cdev = NULL; inode->i_rdev = 0; inode->dirtied_when = 0; - if (security_inode_alloc(inode)) { - if (inode->i_sb->s_op->destroy_inode) - inode->i_sb->s_op->destroy_inode(inode); - else - kmem_cache_free(inode_cachep, (inode)); - return NULL; - } + + if (security_inode_alloc(inode)) + goto out_free_inode; + + /* allocate and initialize an i_integrity */ + if (ima_inode_alloc(inode)) + goto out_free_security; spin_lock_init(&inode->i_lock); lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key); @@ -186,6 +187,15 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode) inode->i_mapping = mapping; return inode; + +out_free_security: + security_inode_free(inode); +out_free_inode: + if (inode->i_sb->s_op->destroy_inode) + inode->i_sb->s_op->destroy_inode(inode); + else + kmem_cache_free(inode_cachep, (inode)); + return NULL; } EXPORT_SYMBOL(inode_init_always); diff --git a/fs/namei.c b/fs/namei.c index af3783fff1de..734f2b5591bf 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -860,6 +861,8 @@ static int __link_path_walk(const char *name, struct nameidata *nd) err = exec_permission_lite(inode); if (err == -EAGAIN) err = vfs_permission(nd, MAY_EXEC); + if (!err) + err = ima_path_check(&nd->path, MAY_EXEC); if (err) break; @@ -1525,6 +1528,11 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) error = vfs_permission(nd, acc_mode); if (error) return error; + + error = ima_path_check(&nd->path, + acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC)); + if (error) + return error; /* * An append-only file must be opened in append mode for writing. */ diff --git a/include/linux/ima.h b/include/linux/ima.h new file mode 100644 index 000000000000..4ed1e4d962e2 --- /dev/null +++ b/include/linux/ima.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 IBM Corporation + * Author: Mimi Zohar + * + * 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, version 2 of the License. + */ + +#include + +#ifndef _LINUX_IMA_H +#define _LINUX_IMA_H + +static inline int ima_bprm_check(struct linux_binprm *bprm) +{ + return 0; +} + +static inline int ima_inode_alloc(struct inode *inode) +{ + return 0; +} + +static inline void ima_inode_free(struct inode *inode) +{ + return; +} + +static inline int ima_path_check(struct path *path, int mask) +{ + return 0; +} + +static inline void ima_file_free(struct file *file) +{ + return; +} + +static inline int ima_file_mmap(struct file *file, unsigned long prot) +{ + return 0; +} +#endif /* _LINUX_IMA_H */ diff --git a/mm/mmap.c b/mm/mmap.c index d4855a682ab6..c3647f3b0621 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1048,6 +1049,9 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, } error = security_file_mmap(file, reqprot, prot, flags, addr, 0); + if (error) + return error; + error = ima_file_mmap(file, prot); if (error) return error; -- cgit v1.2.3-59-g8ed1b From 3323eec921efd815178a23107ab63588c605c0b2 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 4 Feb 2009 09:06:58 -0500 Subject: integrity: IMA as an integrity service provider IMA provides hardware (TPM) based measurement and attestation for file measurements. As the Trusted Computing (TPM) model requires, IMA measures all files before they are accessed in any way (on the integrity_bprm_check, integrity_path_check and integrity_file_mmap hooks), and commits the measurements to the TPM. Once added to the TPM, measurements can not be removed. In addition, IMA maintains a list of these file measurements, which can be used to validate the aggregate value stored in the TPM. The TPM can sign these measurements, and thus the system can prove, to itself and to a third party, the system's integrity in a way that cannot be circumvented by malicious or compromised software. - alloc ima_template_entry before calling ima_store_template() - log ima_add_boot_aggregate() failure - removed unused IMA_TEMPLATE_NAME_LEN - replaced hard coded string length with #define name Signed-off-by: Mimi Zohar Signed-off-by: James Morris --- Documentation/kernel-parameters.txt | 9 ++ include/linux/audit.h | 5 + include/linux/ima.h | 10 ++ security/Kconfig | 5 +- security/Makefile | 4 + security/integrity/ima/Kconfig | 49 +++++++ security/integrity/ima/Makefile | 9 ++ security/integrity/ima/ima.h | 135 +++++++++++++++++ security/integrity/ima/ima_api.c | 190 ++++++++++++++++++++++++ security/integrity/ima/ima_audit.c | 78 ++++++++++ security/integrity/ima/ima_crypto.c | 140 ++++++++++++++++++ security/integrity/ima/ima_iint.c | 185 ++++++++++++++++++++++++ security/integrity/ima/ima_init.c | 90 ++++++++++++ security/integrity/ima/ima_main.c | 280 ++++++++++++++++++++++++++++++++++++ security/integrity/ima/ima_policy.c | 126 ++++++++++++++++ security/integrity/ima/ima_queue.c | 140 ++++++++++++++++++ 16 files changed, 1454 insertions(+), 1 deletion(-) create mode 100644 security/integrity/ima/Kconfig create mode 100644 security/integrity/ima/Makefile create mode 100644 security/integrity/ima/ima.h create mode 100644 security/integrity/ima/ima_api.c create mode 100644 security/integrity/ima/ima_audit.c create mode 100644 security/integrity/ima/ima_crypto.c create mode 100644 security/integrity/ima/ima_iint.c create mode 100644 security/integrity/ima/ima_init.c create mode 100644 security/integrity/ima/ima_main.c create mode 100644 security/integrity/ima/ima_policy.c create mode 100644 security/integrity/ima/ima_queue.c (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7c67b94d1823..31e0c2c3c6e3 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -895,6 +895,15 @@ and is between 256 and 4096 characters. It is defined in the file ihash_entries= [KNL] Set number of hash buckets for inode cache. + ima_audit= [IMA] + Format: { "0" | "1" } + 0 -- integrity auditing messages. (Default) + 1 -- enable informational integrity auditing messages. + + ima_hash= [IMA] + Formt: { "sha1" | "md5" } + default: "sha1" + in2000= [HW,SCSI] See header of drivers/scsi/in2000.c. diff --git a/include/linux/audit.h b/include/linux/audit.h index 26c4f6f65a46..8d1f67789b53 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -125,6 +125,11 @@ #define AUDIT_LAST_KERN_ANOM_MSG 1799 #define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */ #define AUDIT_ANOM_ABEND 1701 /* Process ended abnormally */ +#define AUDIT_INTEGRITY_DATA 1800 /* Data integrity verification */ +#define AUDIT_INTEGRITY_METADATA 1801 /* Metadata integrity verification */ +#define AUDIT_INTEGRITY_STATUS 1802 /* Integrity enable status */ +#define AUDIT_INTEGRITY_HASH 1803 /* Integrity HASH type */ +#define AUDIT_INTEGRITY_PCR 1804 /* PCR invalidation msgs */ #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ diff --git a/include/linux/ima.h b/include/linux/ima.h index 4ed1e4d962e2..dcc3664feee8 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -12,6 +12,15 @@ #ifndef _LINUX_IMA_H #define _LINUX_IMA_H +#ifdef CONFIG_IMA +extern int ima_bprm_check(struct linux_binprm *bprm); +extern int ima_inode_alloc(struct inode *inode); +extern void ima_inode_free(struct inode *inode); +extern int ima_path_check(struct path *path, int mask); +extern void ima_file_free(struct file *file); +extern int ima_file_mmap(struct file *file, unsigned long prot); + +#else static inline int ima_bprm_check(struct linux_binprm *bprm) { return 0; @@ -41,4 +50,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) { return 0; } +#endif /* CONFIG_IMA_H */ #endif /* _LINUX_IMA_H */ diff --git a/security/Kconfig b/security/Kconfig index d9f47ce7e207..a79b23f73d03 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -55,7 +55,8 @@ config SECURITYFS bool "Enable the securityfs filesystem" help This will build the securityfs filesystem. It is currently used by - the TPM bios character driver. It is not used by SELinux or SMACK. + the TPM bios character driver and IMA, an integrity provider. It is + not used by SELinux or SMACK. If you are unsure how to answer this question, answer N. @@ -126,5 +127,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR source security/selinux/Kconfig source security/smack/Kconfig +source security/integrity/ima/Kconfig + endmenu diff --git a/security/Makefile b/security/Makefile index c05c127fff9a..595536cbffb2 100644 --- a/security/Makefile +++ b/security/Makefile @@ -17,3 +17,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o + +# Object integrity file lists +subdir-$(CONFIG_IMA) += integrity/ima +obj-$(CONFIG_IMA) += integrity/ima/built-in.o diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig new file mode 100644 index 000000000000..2a761c8ac996 --- /dev/null +++ b/security/integrity/ima/Kconfig @@ -0,0 +1,49 @@ +# IBM Integrity Measurement Architecture +# +config IMA + bool "Integrity Measurement Architecture(IMA)" + depends on ACPI + select SECURITYFS + select CRYPTO + select CRYPTO_HMAC + select CRYPTO_MD5 + select CRYPTO_SHA1 + select TCG_TPM + select TCG_TIS + help + The Trusted Computing Group(TCG) runtime Integrity + Measurement Architecture(IMA) maintains a list of hash + values of executables and other sensitive system files, + as they are read or executed. If an attacker manages + to change the contents of an important system file + being measured, we can tell. + + If your system has a TPM chip, then IMA also maintains + an aggregate integrity value over this list inside the + TPM hardware, so that the TPM can prove to a third party + whether or not critical system files have been modified. + Read + to learn more about IMA. + If unsure, say N. + +config IMA_MEASURE_PCR_IDX + int + depends on IMA + range 8 14 + default 10 + help + IMA_MEASURE_PCR_IDX determines the TPM PCR register index + that IMA uses to maintain the integrity aggregate of the + measurement list. If unsure, use the default 10. + +config IMA_AUDIT + bool + depends on IMA + default y + help + This option adds a kernel parameter 'ima_audit', which + allows informational auditing messages to be enabled + at boot. If this option is selected, informational integrity + auditing messages can be enabled with 'ima_audit=1' on + the kernel command line. + diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile new file mode 100644 index 000000000000..9d6bf973b9be --- /dev/null +++ b/security/integrity/ima/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for building Trusted Computing Group's(TCG) runtime Integrity +# Measurement Architecture(IMA). +# + +obj-$(CONFIG_IMA) += ima.o + +ima-y := ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ + ima_policy.o ima_iint.o ima_audit.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h new file mode 100644 index 000000000000..bfa72ed41b9b --- /dev/null +++ b/security/integrity/ima/ima.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * + * Authors: + * Reiner Sailer + * Mimi Zohar + * + * 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, version 2 of the + * License. + * + * File: ima.h + * internal Integrity Measurement Architecture (IMA) definitions + */ + +#ifndef __LINUX_IMA_H +#define __LINUX_IMA_H + +#include +#include +#include +#include +#include +#include + +enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII }; +enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; + +/* digest size for IMA, fits SHA1 or MD5 */ +#define IMA_DIGEST_SIZE 20 +#define IMA_EVENT_NAME_LEN_MAX 255 + +#define IMA_HASH_BITS 9 +#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS) + +/* set during initialization */ +extern int ima_initialized; +extern int ima_used_chip; +extern char *ima_hash; + +/* IMA inode template definition */ +struct ima_template_data { + u8 digest[IMA_DIGEST_SIZE]; /* sha1/md5 measurement hash */ + char file_name[IMA_EVENT_NAME_LEN_MAX + 1]; /* name + \0 */ +}; + +struct ima_template_entry { + u8 digest[IMA_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ + char *template_name; + int template_len; + struct ima_template_data template; +}; + +struct ima_queue_entry { + struct hlist_node hnext; /* place in hash collision list */ + struct list_head later; /* place in ima_measurements list */ + struct ima_template_entry *entry; +}; +extern struct list_head ima_measurements; /* list of all measurements */ + +/* declarations */ +void integrity_audit_msg(int audit_msgno, struct inode *inode, + const unsigned char *fname, const char *op, + const char *cause, int result, int info); + +/* Internal IMA function definitions */ +void ima_iintcache_init(void); +int ima_init(void); +int ima_add_template_entry(struct ima_template_entry *entry, int violation, + const char *op, struct inode *inode); +int ima_calc_hash(struct file *file, char *digest); +int ima_calc_template_hash(int template_len, void *template, char *digest); +int ima_calc_boot_aggregate(char *digest); +void ima_add_violation(struct inode *inode, const unsigned char *filename, + const char *op, const char *cause); + +/* + * used to protect h_table and sha_table + */ +extern spinlock_t ima_queue_lock; + +struct ima_h_table { + atomic_long_t len; /* number of stored measurements in the list */ + atomic_long_t violations; + struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; +}; +extern struct ima_h_table ima_htable; + +static inline unsigned long ima_hash_key(u8 *digest) +{ + return hash_long(*digest, IMA_HASH_BITS); +} + +/* iint cache flags */ +#define IMA_MEASURED 1 + +/* integrity data associated with an inode */ +struct ima_iint_cache { + u64 version; /* track inode changes */ + unsigned long flags; + u8 digest[IMA_DIGEST_SIZE]; + struct mutex mutex; /* protects: version, flags, digest */ + long readcount; /* measured files readcount */ + long writecount; /* measured files writecount */ + struct kref refcount; /* ima_iint_cache reference count */ + struct rcu_head rcu; +}; + +/* LIM API function definitions */ +int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode, + int mask, int function); +int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file); +void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, + const unsigned char *filename); +int ima_store_template(struct ima_template_entry *entry, int violation, + struct inode *inode); + +/* radix tree calls to lookup, insert, delete + * integrity data associated with an inode. + */ +struct ima_iint_cache *ima_iint_insert(struct inode *inode); +struct ima_iint_cache *ima_iint_find_get(struct inode *inode); +struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode); +void ima_iint_delete(struct inode *inode); +void iint_free(struct kref *kref); +void iint_rcu_free(struct rcu_head *rcu); + +/* IMA policy related functions */ +enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK }; + +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask); +void ima_init_policy(void); +void ima_update_policy(void); +#endif diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c new file mode 100644 index 000000000000..a148a25804f6 --- /dev/null +++ b/security/integrity/ima/ima_api.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2008 IBM Corporation + * + * Author: Mimi Zohar + * + * 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, version 2 of the + * License. + * + * File: ima_api.c + * Implements must_measure, collect_measurement, store_measurement, + * and store_template. + */ +#include + +#include "ima.h" +static char *IMA_TEMPLATE_NAME = "ima"; + +/* + * ima_store_template - store ima template measurements + * + * Calculate the hash of a template entry, add the template entry + * to an ordered list of measurement entries maintained inside the kernel, + * and also update the aggregate integrity value (maintained inside the + * configured TPM PCR) over the hashes of the current list of measurement + * entries. + * + * Applications retrieve the current kernel-held measurement list through + * the securityfs entries in /sys/kernel/security/ima. The signed aggregate + * TPM PCR (called quote) can be retrieved using a TPM user space library + * and is used to validate the measurement list. + * + * Returns 0 on success, error code otherwise + */ +int ima_store_template(struct ima_template_entry *entry, + int violation, struct inode *inode) +{ + const char *op = "add_template_measure"; + const char *audit_cause = "hashing_error"; + int result; + + memset(entry->digest, 0, sizeof(entry->digest)); + entry->template_name = IMA_TEMPLATE_NAME; + entry->template_len = sizeof(entry->template); + + if (!violation) { + result = ima_calc_template_hash(entry->template_len, + &entry->template, + entry->digest); + if (result < 0) { + integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, + entry->template_name, op, + audit_cause, result, 0); + return result; + } + } + result = ima_add_template_entry(entry, violation, op, inode); + return result; +} + +/* + * ima_add_violation - add violation to measurement list. + * + * Violations are flagged in the measurement list with zero hash values. + * By extending the PCR with 0xFF's instead of with zeroes, the PCR + * value is invalidated. + */ +void ima_add_violation(struct inode *inode, const unsigned char *filename, + const char *op, const char *cause) +{ + struct ima_template_entry *entry; + int violation = 1; + int result; + + /* can overflow, only indicator */ + atomic_long_inc(&ima_htable.violations); + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + result = -ENOMEM; + goto err_out; + } + memset(&entry->template, 0, sizeof(entry->template)); + strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); + result = ima_store_template(entry, violation, inode); + if (result < 0) + kfree(entry); +err_out: + integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, + op, cause, result, 0); +} + +/** + * ima_must_measure - measure decision based on policy. + * @inode: pointer to inode to measure + * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) + * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP) + * + * The policy is defined in terms of keypairs: + * subj=, obj=, type=, func=, mask=, fsmagic= + * subj,obj, and type: are LSM specific. + * func: PATH_CHECK | BPRM_CHECK | FILE_MMAP + * mask: contains the permission mask + * fsmagic: hex value + * + * Must be called with iint->mutex held. + * + * Return 0 to measure. Return 1 if already measured. + * For matching a DONT_MEASURE policy, no policy, or other + * error, return an error code. +*/ +int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode, + int mask, int function) +{ + int must_measure; + + if (iint->flags & IMA_MEASURED) + return 1; + + must_measure = ima_match_policy(inode, function, mask); + return must_measure ? 0 : -EACCES; +} + +/* + * ima_collect_measurement - collect file measurement + * + * Calculate the file hash, if it doesn't already exist, + * storing the measurement and i_version in the iint. + * + * Must be called with iint->mutex held. + * + * Return 0 on success, error code otherwise + */ +int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file) +{ + int result = -EEXIST; + + if (!(iint->flags & IMA_MEASURED)) { + u64 i_version = file->f_dentry->d_inode->i_version; + + memset(iint->digest, 0, IMA_DIGEST_SIZE); + result = ima_calc_hash(file, iint->digest); + if (!result) + iint->version = i_version; + } + return result; +} + +/* + * ima_store_measurement - store file measurement + * + * Create an "ima" template and then store the template by calling + * ima_store_template. + * + * We only get here if the inode has not already been measured, + * but the measurement could already exist: + * - multiple copies of the same file on either the same or + * different filesystems. + * - the inode was previously flushed as well as the iint info, + * containing the hashing info. + * + * Must be called with iint->mutex held. + */ +void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, + const unsigned char *filename) +{ + const char *op = "add_template_measure"; + const char *audit_cause = "ENOMEM"; + int result = -ENOMEM; + struct inode *inode = file->f_dentry->d_inode; + struct ima_template_entry *entry; + int violation = 0; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, + op, audit_cause, result, 0); + return; + } + memset(&entry->template, 0, sizeof(entry->template)); + memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE); + strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX); + + result = ima_store_template(entry, violation, inode); + if (!result) + iint->flags |= IMA_MEASURED; + else + kfree(entry); +} diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c new file mode 100644 index 000000000000..8a0f1e23ccf1 --- /dev/null +++ b/security/integrity/ima/ima_audit.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 IBM Corporation + * Author: Mimi Zohar + * + * 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, version 2 of the License. + * + * File: integrity_audit.c + * Audit calls for the integrity subsystem + */ + +#include +#include +#include "ima.h" + +static int ima_audit; + +#ifdef CONFIG_IMA_AUDIT + +/* ima_audit_setup - enable informational auditing messages */ +static int __init ima_audit_setup(char *str) +{ + unsigned long audit; + int rc; + char *op; + + rc = strict_strtoul(str, 0, &audit); + if (rc || audit > 1) + printk(KERN_INFO "ima: invalid ima_audit value\n"); + else + ima_audit = audit; + op = ima_audit ? "ima_audit_enabled" : "ima_audit_not_enabled"; + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, NULL, op, 0, 0); + return 1; +} +__setup("ima_audit=", ima_audit_setup); +#endif + +void integrity_audit_msg(int audit_msgno, struct inode *inode, + const unsigned char *fname, const char *op, + const char *cause, int result, int audit_info) +{ + struct audit_buffer *ab; + + if (!ima_audit && audit_info == 1) /* Skip informational messages */ + return; + + ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno); + audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u", + current->pid, current->cred->uid, + audit_get_loginuid(current)); + audit_log_task_context(ab); + switch (audit_msgno) { + case AUDIT_INTEGRITY_DATA: + case AUDIT_INTEGRITY_METADATA: + case AUDIT_INTEGRITY_PCR: + audit_log_format(ab, " op=%s cause=%s", op, cause); + break; + case AUDIT_INTEGRITY_HASH: + audit_log_format(ab, " op=%s hash=%s", op, cause); + break; + case AUDIT_INTEGRITY_STATUS: + default: + audit_log_format(ab, " op=%s", op); + } + audit_log_format(ab, " comm="); + audit_log_untrustedstring(ab, current->comm); + if (fname) { + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, fname); + } + if (inode) + audit_log_format(ab, " dev=%s ino=%lu", + inode->i_sb->s_id, inode->i_ino); + audit_log_format(ab, " res=%d", result); + audit_log_end(ab); +} diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c new file mode 100644 index 000000000000..c2a46e40999d --- /dev/null +++ b/security/integrity/ima/ima_crypto.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * + * Authors: + * Mimi Zohar + * Kylene Hall + * + * 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, version 2 of the License. + * + * File: ima_crypto.c + * Calculates md5/sha1 file hash, template hash, boot-aggreate hash + */ + +#include +#include +#include +#include +#include +#include "ima.h" + +static int init_desc(struct hash_desc *desc) +{ + int rc; + + desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(desc->tfm)) { + pr_info("failed to load %s transform: %ld\n", + ima_hash, PTR_ERR(desc->tfm)); + rc = PTR_ERR(desc->tfm); + return rc; + } + desc->flags = 0; + rc = crypto_hash_init(desc); + if (rc) + crypto_free_hash(desc->tfm); + return rc; +} + +/* + * Calculate the MD5/SHA1 file digest + */ +int ima_calc_hash(struct file *file, char *digest) +{ + struct hash_desc desc; + struct scatterlist sg[1]; + loff_t i_size; + char *rbuf; + int rc, offset = 0; + + rc = init_desc(&desc); + if (rc != 0) + return rc; + + rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!rbuf) { + rc = -ENOMEM; + goto out; + } + i_size = i_size_read(file->f_dentry->d_inode); + while (offset < i_size) { + int rbuf_len; + + rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE); + if (rbuf_len < 0) { + rc = rbuf_len; + break; + } + offset += rbuf_len; + sg_set_buf(sg, rbuf, rbuf_len); + + rc = crypto_hash_update(&desc, sg, rbuf_len); + if (rc) + break; + } + kfree(rbuf); + if (!rc) + rc = crypto_hash_final(&desc, digest); +out: + crypto_free_hash(desc.tfm); + return rc; +} + +/* + * Calculate the hash of a given template + */ +int ima_calc_template_hash(int template_len, void *template, char *digest) +{ + struct hash_desc desc; + struct scatterlist sg[1]; + int rc; + + rc = init_desc(&desc); + if (rc != 0) + return rc; + + sg_set_buf(sg, template, template_len); + rc = crypto_hash_update(&desc, sg, template_len); + if (!rc) + rc = crypto_hash_final(&desc, digest); + crypto_free_hash(desc.tfm); + return rc; +} + +static void ima_pcrread(int idx, u8 *pcr) +{ + if (!ima_used_chip) + return; + + if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0) + pr_err("Error Communicating to TPM chip\n"); +} + +/* + * Calculate the boot aggregate hash + */ +int ima_calc_boot_aggregate(char *digest) +{ + struct hash_desc desc; + struct scatterlist sg; + u8 pcr_i[IMA_DIGEST_SIZE]; + int rc, i; + + rc = init_desc(&desc); + if (rc != 0) + return rc; + + /* cumulative sha1 over tpm registers 0-7 */ + for (i = TPM_PCR0; i < TPM_PCR8; i++) { + ima_pcrread(i, pcr_i); + /* now accumulate with current aggregate */ + sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE); + rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE); + } + if (!rc) + crypto_hash_final(&desc, digest); + crypto_free_hash(desc.tfm); + return rc; +} diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c new file mode 100644 index 000000000000..750db3c993a7 --- /dev/null +++ b/security/integrity/ima/ima_iint.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2008 IBM Corporation + * + * Authors: + * Mimi Zohar + * + * 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, version 2 of the + * License. + * + * File: ima_iint.c + * - implements the IMA hooks: ima_inode_alloc, ima_inode_free + * - cache integrity information associated with an inode + * using a radix tree. + */ +#include +#include +#include +#include "ima.h" + +#define ima_iint_delete ima_inode_free + +RADIX_TREE(ima_iint_store, GFP_ATOMIC); +DEFINE_SPINLOCK(ima_iint_lock); + +static struct kmem_cache *iint_cache __read_mostly; + +/* ima_iint_find_get - return the iint associated with an inode + * + * ima_iint_find_get gets a reference to the iint. Caller must + * remember to put the iint reference. + */ +struct ima_iint_cache *ima_iint_find_get(struct inode *inode) +{ + struct ima_iint_cache *iint; + + rcu_read_lock(); + iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode); + if (!iint) + goto out; + kref_get(&iint->refcount); +out: + rcu_read_unlock(); + return iint; +} + +/* Allocate memory for the iint associated with the inode + * from the iint_cache slab, initialize the iint, and + * insert it into the radix tree. + * + * On success return a pointer to the iint; on failure return NULL. + */ +struct ima_iint_cache *ima_iint_insert(struct inode *inode) +{ + struct ima_iint_cache *iint = NULL; + int rc = 0; + + if (!ima_initialized) + return iint; + iint = kmem_cache_alloc(iint_cache, GFP_KERNEL); + if (!iint) + return iint; + + rc = radix_tree_preload(GFP_KERNEL); + if (rc < 0) + goto out; + + spin_lock(&ima_iint_lock); + rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint); + spin_unlock(&ima_iint_lock); +out: + if (rc < 0) { + kmem_cache_free(iint_cache, iint); + if (rc == -EEXIST) { + iint = radix_tree_lookup(&ima_iint_store, + (unsigned long)inode); + } else + iint = NULL; + } + radix_tree_preload_end(); + return iint; +} + +/** + * ima_inode_alloc - allocate an iint associated with an inode + * @inode: pointer to the inode + * + * Return 0 on success, 1 on failure. + */ +int ima_inode_alloc(struct inode *inode) +{ + struct ima_iint_cache *iint; + + if (!ima_initialized) + return 0; + + iint = ima_iint_insert(inode); + if (!iint) + return 1; + return 0; +} + +/* ima_iint_find_insert_get - get the iint associated with an inode + * + * Most insertions are done at inode_alloc, except those allocated + * before late_initcall. When the iint does not exist, allocate it, + * initialize and insert it, and increment the iint refcount. + * + * (Can't initialize at security_initcall before any inodes are + * allocated, got to wait at least until proc_init.) + * + * Return the iint. + */ +struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode) +{ + struct ima_iint_cache *iint = NULL; + + iint = ima_iint_find_get(inode); + if (iint) + return iint; + + iint = ima_iint_insert(inode); + if (iint) + kref_get(&iint->refcount); + + return iint; +} + +/* iint_free - called when the iint refcount goes to zero */ +void iint_free(struct kref *kref) +{ + struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache, + refcount); + iint->version = 0; + iint->flags = 0UL; + kref_set(&iint->refcount, 1); + kmem_cache_free(iint_cache, iint); +} + +void iint_rcu_free(struct rcu_head *rcu_head) +{ + struct ima_iint_cache *iint = container_of(rcu_head, + struct ima_iint_cache, rcu); + kref_put(&iint->refcount, iint_free); +} + +/** + * ima_iint_delete - called on integrity_inode_free + * @inode: pointer to the inode + * + * Free the integrity information(iint) associated with an inode. + */ +void ima_iint_delete(struct inode *inode) +{ + struct ima_iint_cache *iint; + + if (!ima_initialized) + return; + spin_lock(&ima_iint_lock); + iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode); + spin_unlock(&ima_iint_lock); + if (iint) + call_rcu(&iint->rcu, iint_rcu_free); +} + +static void init_once(void *foo) +{ + struct ima_iint_cache *iint = foo; + + memset(iint, 0, sizeof *iint); + iint->version = 0; + iint->flags = 0UL; + mutex_init(&iint->mutex); + iint->readcount = 0; + iint->writecount = 0; + kref_set(&iint->refcount, 1); +} + +void ima_iintcache_init(void) +{ + iint_cache = + kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0, + SLAB_PANIC, init_once); +} diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c new file mode 100644 index 000000000000..e0f02e328d77 --- /dev/null +++ b/security/integrity/ima/ima_init.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * + * Authors: + * Reiner Sailer + * Leendert van Doorn + * Mimi Zohar + * + * 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, version 2 of the + * License. + * + * File: ima_init.c + * initialization and cleanup functions + */ +#include +#include +#include +#include "ima.h" + +/* name for boot aggregate entry */ +static char *boot_aggregate_name = "boot_aggregate"; +int ima_used_chip; + +/* Add the boot aggregate to the IMA measurement list and extend + * the PCR register. + * + * Calculate the boot aggregate, a SHA1 over tpm registers 0-7, + * assuming a TPM chip exists, and zeroes if the TPM chip does not + * exist. Add the boot aggregate measurement to the measurement + * list and extend the PCR register. + * + * If a tpm chip does not exist, indicate the core root of trust is + * not hardware based by invalidating the aggregate PCR value. + * (The aggregate PCR value is invalidated by adding one value to + * the measurement list and extending the aggregate PCR value with + * a different value.) Violations add a zero entry to the measurement + * list and extend the aggregate PCR value with ff...ff's. + */ +static void ima_add_boot_aggregate(void) +{ + struct ima_template_entry *entry; + const char *op = "add_boot_aggregate"; + const char *audit_cause = "ENOMEM"; + int result = -ENOMEM; + int violation = 1; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + goto err_out; + + memset(&entry->template, 0, sizeof(entry->template)); + strncpy(entry->template.file_name, boot_aggregate_name, + IMA_EVENT_NAME_LEN_MAX); + if (ima_used_chip) { + violation = 0; + result = ima_calc_boot_aggregate(entry->template.digest); + if (result < 0) { + audit_cause = "hashing_error"; + kfree(entry); + goto err_out; + } + } + result = ima_store_template(entry, violation, NULL); + if (result < 0) + kfree(entry); + return; +err_out: + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op, + audit_cause, result, 0); +} + +int ima_init(void) +{ + u8 pcr_i[IMA_DIGEST_SIZE]; + int rc; + + ima_used_chip = 0; + rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i); + if (rc == 0) + ima_used_chip = 1; + + if (!ima_used_chip) + pr_info("No TPM chip found, activating TPM-bypass!\n"); + + ima_add_boot_aggregate(); /* boot aggregate must be first entry */ + ima_init_policy(); + return 0; +} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c new file mode 100644 index 000000000000..53cee4c512ce --- /dev/null +++ b/security/integrity/ima/ima_main.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * + * Authors: + * Reiner Sailer + * Serge Hallyn + * Kylene Hall + * Mimi Zohar + * + * 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, version 2 of the + * License. + * + * File: ima_main.c + * implements the IMA hooks: ima_bprm_check, ima_file_mmap, + * and ima_path_check. + */ +#include +#include +#include +#include +#include + +#include "ima.h" + +int ima_initialized; + +char *ima_hash = "sha1"; +static int __init hash_setup(char *str) +{ + const char *op = "hash_setup"; + const char *hash = "sha1"; + int result = 0; + int audit_info = 0; + + if (strncmp(str, "md5", 3) == 0) { + hash = "md5"; + ima_hash = str; + } else if (strncmp(str, "sha1", 4) != 0) { + hash = "invalid_hash_type"; + result = 1; + } + integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash, + result, audit_info); + return 1; +} +__setup("ima_hash=", hash_setup); + +/** + * ima_file_free - called on __fput() + * @file: pointer to file structure being freed + * + * Flag files that changed, based on i_version; + * and decrement the iint readcount/writecount. + */ +void ima_file_free(struct file *file) +{ + struct inode *inode = file->f_dentry->d_inode; + struct ima_iint_cache *iint; + + if (!ima_initialized || !S_ISREG(inode->i_mode)) + return; + iint = ima_iint_find_get(inode); + if (!iint) + return; + + mutex_lock(&iint->mutex); + if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) + iint->readcount--; + + if (file->f_mode & FMODE_WRITE) { + iint->writecount--; + if (iint->writecount == 0) { + if (iint->version != inode->i_version) + iint->flags &= ~IMA_MEASURED; + } + } + mutex_unlock(&iint->mutex); + kref_put(&iint->refcount, iint_free); +} + +/* ima_read_write_check - reflect possible reading/writing errors in the PCR. + * + * When opening a file for read, if the file is already open for write, + * the file could change, resulting in a file measurement error. + * + * Opening a file for write, if the file is already open for read, results + * in a time of measure, time of use (ToMToU) error. + * + * In either case invalidate the PCR. + */ +enum iint_pcr_error { TOMTOU, OPEN_WRITERS }; +static void ima_read_write_check(enum iint_pcr_error error, + struct ima_iint_cache *iint, + struct inode *inode, + const unsigned char *filename) +{ + switch (error) { + case TOMTOU: + if (iint->readcount > 0) + ima_add_violation(inode, filename, "invalid_pcr", + "ToMToU"); + break; + case OPEN_WRITERS: + if (iint->writecount > 0) + ima_add_violation(inode, filename, "invalid_pcr", + "open_writers"); + break; + } +} + +static int get_path_measurement(struct ima_iint_cache *iint, struct file *file, + const unsigned char *filename) +{ + int rc = 0; + + if (IS_ERR(file)) { + pr_info("%s dentry_open failed\n", filename); + return rc; + } + iint->readcount++; + + rc = ima_collect_measurement(iint, file); + if (!rc) + ima_store_measurement(iint, file, filename); + return rc; +} + +/** + * ima_path_check - based on policy, collect/store measurement. + * @path: contains a pointer to the path to be measured + * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE + * + * Measure the file being open for readonly, based on the + * ima_must_measure() policy decision. + * + * Keep read/write counters for all files, but only + * invalidate the PCR for measured files: + * - Opening a file for write when already open for read, + * results in a time of measure, time of use (ToMToU) error. + * - Opening a file for read when already open for write, + * could result in a file measurement error. + * + * Return 0 on success, an error code on failure. + * (Based on the results of appraise_measurement().) + */ +int ima_path_check(struct path *path, int mask) +{ + struct inode *inode = path->dentry->d_inode; + struct ima_iint_cache *iint; + struct file *file = NULL; + int rc; + + if (!ima_initialized || !S_ISREG(inode->i_mode)) + return 0; + iint = ima_iint_find_insert_get(inode); + if (!iint) + return 0; + + mutex_lock(&iint->mutex); + if ((mask & MAY_WRITE) || (mask == 0)) + iint->writecount++; + else if (mask & (MAY_READ | MAY_EXEC)) + iint->readcount++; + + rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK); + if (rc < 0) + goto out; + + if ((mask & MAY_WRITE) || (mask == 0)) + ima_read_write_check(TOMTOU, iint, inode, + path->dentry->d_name.name); + + if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ) + goto out; + + ima_read_write_check(OPEN_WRITERS, iint, inode, + path->dentry->d_name.name); + if (!(iint->flags & IMA_MEASURED)) { + struct dentry *dentry = dget(path->dentry); + struct vfsmount *mnt = mntget(path->mnt); + + file = dentry_open(dentry, mnt, O_RDONLY, current->cred); + rc = get_path_measurement(iint, file, dentry->d_name.name); + } +out: + mutex_unlock(&iint->mutex); + if (file) + fput(file); + kref_put(&iint->refcount, iint_free); + return 0; +} + +static int process_measurement(struct file *file, const unsigned char *filename, + int mask, int function) +{ + struct inode *inode = file->f_dentry->d_inode; + struct ima_iint_cache *iint; + int rc; + + if (!ima_initialized || !S_ISREG(inode->i_mode)) + return 0; + iint = ima_iint_find_insert_get(inode); + if (!iint) + return -ENOMEM; + + mutex_lock(&iint->mutex); + rc = ima_must_measure(iint, inode, mask, function); + if (rc != 0) + goto out; + + rc = ima_collect_measurement(iint, file); + if (!rc) + ima_store_measurement(iint, file, filename); +out: + mutex_unlock(&iint->mutex); + kref_put(&iint->refcount, iint_free); + return rc; +} + +/** + * ima_file_mmap - based on policy, collect/store measurement. + * @file: pointer to the file to be measured (May be NULL) + * @prot: contains the protection that will be applied by the kernel. + * + * Measure files being mmapped executable based on the ima_must_measure() + * policy decision. + * + * Return 0 on success, an error code on failure. + * (Based on the results of appraise_measurement().) + */ +int ima_file_mmap(struct file *file, unsigned long prot) +{ + int rc; + + if (!file) + return 0; + if (prot & PROT_EXEC) + rc = process_measurement(file, file->f_dentry->d_name.name, + MAY_EXEC, FILE_MMAP); + return 0; +} + +/** + * ima_bprm_check - based on policy, collect/store measurement. + * @bprm: contains the linux_binprm structure + * + * The OS protects against an executable file, already open for write, + * from being executed in deny_write_access() and an executable file, + * already open for execute, from being modified in get_write_access(). + * So we can be certain that what we verify and measure here is actually + * what is being executed. + * + * Return 0 on success, an error code on failure. + * (Based on the results of appraise_measurement().) + */ +int ima_bprm_check(struct linux_binprm *bprm) +{ + int rc; + + rc = process_measurement(bprm->file, bprm->filename, + MAY_EXEC, BPRM_CHECK); + return 0; +} + +static int __init init_ima(void) +{ + int error; + + ima_iintcache_init(); + error = ima_init(); + ima_initialized = 1; + return error; +} + +late_initcall(init_ima); /* Start IMA after the TPM is available */ + +MODULE_DESCRIPTION("Integrity Measurement Architecture"); +MODULE_LICENSE("GPL"); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c new file mode 100644 index 000000000000..7c3d1ffb1472 --- /dev/null +++ b/security/integrity/ima/ima_policy.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2008 IBM Corporation + * Author: Mimi Zohar + * + * 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, version 2 of the License. + * + * ima_policy.c + * - initialize default measure policy rules + * + */ +#include +#include +#include +#include +#include + +#include "ima.h" + +/* flags definitions */ +#define IMA_FUNC 0x0001 +#define IMA_MASK 0x0002 +#define IMA_FSMAGIC 0x0004 +#define IMA_UID 0x0008 + +enum ima_action { DONT_MEASURE, MEASURE }; + +struct ima_measure_rule_entry { + struct list_head list; + enum ima_action action; + unsigned int flags; + enum ima_hooks func; + int mask; + unsigned long fsmagic; + uid_t uid; +}; + +static struct ima_measure_rule_entry default_rules[] = { + {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC, + .flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC, + .flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC}, + {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC, + .flags = IMA_FUNC | IMA_MASK}, + {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, + .flags = IMA_FUNC | IMA_MASK}, + {.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0, + .flags = IMA_FUNC | IMA_MASK | IMA_UID} +}; + +static LIST_HEAD(measure_default_rules); +static struct list_head *ima_measure; + +/** + * ima_match_rules - determine whether an inode matches the measure rule. + * @rule: a pointer to a rule + * @inode: a pointer to an inode + * @func: LIM hook identifier + * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) + * + * Returns true on rule match, false on failure. + */ +static bool ima_match_rules(struct ima_measure_rule_entry *rule, + struct inode *inode, enum ima_hooks func, int mask) +{ + struct task_struct *tsk = current; + + if ((rule->flags & IMA_FUNC) && rule->func != func) + return false; + if ((rule->flags & IMA_MASK) && rule->mask != mask) + return false; + if ((rule->flags & IMA_FSMAGIC) + && rule->fsmagic != inode->i_sb->s_magic) + return false; + if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid) + return false; + return true; +} + +/** + * ima_match_policy - decision based on LSM and other conditions + * @inode: pointer to an inode for which the policy decision is being made + * @func: IMA hook identifier + * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) + * + * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type) + * conditions. + * + * (There is no need for locking when walking the policy list, + * as elements in the list are never deleted, nor does the list + * change.) + */ +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask) +{ + struct ima_measure_rule_entry *entry; + + list_for_each_entry(entry, ima_measure, list) { + bool rc; + + rc = ima_match_rules(entry, inode, func, mask); + if (rc) + return entry->action; + } + return 0; +} + +/** + * ima_init_policy - initialize the default measure rules. + * + * (Could use the default_rules directly, but in policy patch + * ima_measure points to either the measure_default_rules or the + * the new measure_policy_rules.) + */ +void ima_init_policy(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(default_rules); i++) + list_add_tail(&default_rules[i].list, &measure_default_rules); + ima_measure = &measure_default_rules; +} diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c new file mode 100644 index 000000000000..7ec94314ac0c --- /dev/null +++ b/security/integrity/ima/ima_queue.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005,2006,2007,2008 IBM Corporation + * + * Authors: + * Serge Hallyn + * Reiner Sailer + * Mimi Zohar + * + * 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, version 2 of the + * License. + * + * File: ima_queue.c + * Implements queues that store template measurements and + * maintains aggregate over the stored measurements + * in the pre-configured TPM PCR (if available). + * The measurement list is append-only. No entry is + * ever removed or changed during the boot-cycle. + */ +#include +#include +#include "ima.h" + +LIST_HEAD(ima_measurements); /* list of all measurements */ + +/* key: inode (before secure-hashing a file) */ +struct ima_h_table ima_htable = { + .len = ATOMIC_LONG_INIT(0), + .violations = ATOMIC_LONG_INIT(0), + .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT +}; + +/* mutex protects atomicity of extending measurement list + * and extending the TPM PCR aggregate. Since tpm_extend can take + * long (and the tpm driver uses a mutex), we can't use the spinlock. + */ +static DEFINE_MUTEX(ima_extend_list_mutex); + +/* lookup up the digest value in the hash table, and return the entry */ +static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value) +{ + struct ima_queue_entry *qe, *ret = NULL; + unsigned int key; + struct hlist_node *pos; + int rc; + + key = ima_hash_key(digest_value); + rcu_read_lock(); + hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) { + rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE); + if (rc == 0) { + ret = qe; + break; + } + } + rcu_read_unlock(); + return ret; +} + +/* ima_add_template_entry helper function: + * - Add template entry to measurement list and hash table. + * + * (Called with ima_extend_list_mutex held.) + */ +static int ima_add_digest_entry(struct ima_template_entry *entry) +{ + struct ima_queue_entry *qe; + unsigned int key; + + qe = kmalloc(sizeof(*qe), GFP_KERNEL); + if (qe == NULL) { + pr_err("OUT OF MEMORY ERROR creating queue entry.\n"); + return -ENOMEM; + } + qe->entry = entry; + + INIT_LIST_HEAD(&qe->later); + list_add_tail_rcu(&qe->later, &ima_measurements); + + atomic_long_inc(&ima_htable.len); + key = ima_hash_key(entry->digest); + hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]); + return 0; +} + +static int ima_pcr_extend(const u8 *hash) +{ + int result = 0; + + if (!ima_used_chip) + return result; + + result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash); + if (result != 0) + pr_err("Error Communicating to TPM chip\n"); + return result; +} + +/* Add template entry to the measurement list and hash table, + * and extend the pcr. + */ +int ima_add_template_entry(struct ima_template_entry *entry, int violation, + const char *op, struct inode *inode) +{ + u8 digest[IMA_DIGEST_SIZE]; + const char *audit_cause = "hash_added"; + int audit_info = 1; + int result = 0; + + mutex_lock(&ima_extend_list_mutex); + if (!violation) { + memcpy(digest, entry->digest, sizeof digest); + if (ima_lookup_digest_entry(digest)) { + audit_cause = "hash_exists"; + goto out; + } + } + + result = ima_add_digest_entry(entry); + if (result < 0) { + audit_cause = "ENOMEM"; + audit_info = 0; + goto out; + } + + if (violation) /* invalidate pcr */ + memset(digest, 0xff, sizeof digest); + + result = ima_pcr_extend(digest); + if (result != 0) { + audit_cause = "TPM error"; + audit_info = 0; + } +out: + mutex_unlock(&ima_extend_list_mutex); + integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name, + op, audit_cause, result, audit_info); + return result; +} -- cgit v1.2.3-59-g8ed1b From 4af4662fa4a9dc62289c580337ae2506339c4729 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 4 Feb 2009 09:07:00 -0500 Subject: integrity: IMA policy Support for a user loadable policy through securityfs with support for LSM specific policy data. - free invalid rule in ima_parse_add_rule() Signed-off-by: Mimi Zohar Acked-by: Serge Hallyn Signed-off-by: James Morris --- Documentation/ABI/testing/ima_policy | 61 ++++++++ security/integrity/ima/Kconfig | 6 + security/integrity/ima/ima.h | 24 +++ security/integrity/ima/ima_fs.c | 67 +++++++- security/integrity/ima/ima_policy.c | 293 ++++++++++++++++++++++++++++++++++- 5 files changed, 447 insertions(+), 4 deletions(-) create mode 100644 Documentation/ABI/testing/ima_policy (limited to 'Documentation') diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy new file mode 100644 index 000000000000..6434f0df012e --- /dev/null +++ b/Documentation/ABI/testing/ima_policy @@ -0,0 +1,61 @@ +What: security/ima/policy +Date: May 2008 +Contact: Mimi Zohar +Description: + The Trusted Computing Group(TCG) runtime Integrity + Measurement Architecture(IMA) maintains a list of hash + values of executables and other sensitive system files + loaded into the run-time of this system. At runtime, + the policy can be constrained based on LSM specific data. + Policies are loaded into the securityfs file ima/policy + by opening the file, writing the rules one at a time and + then closing the file. The new policy takes effect after + the file ima/policy is closed. + + rule format: action [condition ...] + + action: measure | dont_measure + condition:= base | lsm + base: [[func=] [mask=] [fsmagic=] [uid=]] + lsm: [[subj_user=] [subj_role=] [subj_type=] + [obj_user=] [obj_role=] [obj_type=]] + + base: func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION] + mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] + fsmagic:= hex value + uid:= decimal value + lsm: are LSM specific + + default policy: + # PROC_SUPER_MAGIC + dont_measure fsmagic=0x9fa0 + # SYSFS_MAGIC + dont_measure fsmagic=0x62656572 + # DEBUGFS_MAGIC + dont_measure fsmagic=0x64626720 + # TMPFS_MAGIC + dont_measure fsmagic=0x01021994 + # SECURITYFS_MAGIC + dont_measure fsmagic=0x73636673 + + measure func=BPRM_CHECK + measure func=FILE_MMAP mask=MAY_EXEC + measure func=INODE_PERM mask=MAY_READ uid=0 + + The default policy measures all executables in bprm_check, + all files mmapped executable in file_mmap, and all files + open for read by root in inode_permission. + + Examples of LSM specific definitions: + + SELinux: + # SELINUX_MAGIC + dont_measure fsmagic=0xF97CFF8C + + dont_measure obj_type=var_log_t + dont_measure obj_type=auditd_log_t + measure subj_user=system_u func=INODE_PERM mask=MAY_READ + measure subj_role=system_r func=INODE_PERM mask=MAY_READ + + Smack: + measure subj_user=_ func=INODE_PERM mask=MAY_READ diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 2a761c8ac996..3d2b6ee778a0 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -47,3 +47,9 @@ config IMA_AUDIT auditing messages can be enabled with 'ima_audit=1' on the kernel command line. +config IMA_LSM_RULES + bool + depends on IMA && (SECURITY_SELINUX || SECURITY_SMACK) + default y + help + Disabling this option will disregard LSM based policy rules diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 9c280cc73004..42706b554921 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -137,4 +137,28 @@ enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK }; int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask); void ima_init_policy(void); void ima_update_policy(void); +int ima_parse_add_rule(char *); +void ima_delete_rules(void); + +/* LSM based policy rules require audit */ +#ifdef CONFIG_IMA_LSM_RULES + +#define security_filter_rule_init security_audit_rule_init +#define security_filter_rule_match security_audit_rule_match + +#else + +static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr, + void **lsmrule) +{ + return -EINVAL; +} + +static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, + void *lsmrule, + struct audit_context *actx) +{ + return -EINVAL; +} +#endif /* CONFIG_IMA_LSM_RULES */ #endif diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 4f25be768b50..95ef1caa64b5 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -19,9 +19,11 @@ #include #include #include +#include #include "ima.h" +static int valid_policy = 1; #define TMPBUFLEN 12 static ssize_t ima_show_htable_value(char __user *buf, size_t count, loff_t *ppos, atomic_long_t *val) @@ -237,11 +239,66 @@ static struct file_operations ima_ascii_measurements_ops = { .release = seq_release, }; +static ssize_t ima_write_policy(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +{ + char *data; + int rc; + + if (datalen >= PAGE_SIZE) + return -ENOMEM; + if (*ppos != 0) { + /* No partial writes. */ + return -EINVAL; + } + data = kmalloc(datalen + 1, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (copy_from_user(data, buf, datalen)) { + kfree(data); + return -EFAULT; + } + *(data + datalen) = '\0'; + rc = ima_parse_add_rule(data); + if (rc < 0) { + datalen = -EINVAL; + valid_policy = 0; + } + + kfree(data); + return datalen; +} + static struct dentry *ima_dir; static struct dentry *binary_runtime_measurements; static struct dentry *ascii_runtime_measurements; static struct dentry *runtime_measurements_count; static struct dentry *violations; +static struct dentry *ima_policy; + +/* + * ima_release_policy - start using the new measure policy rules. + * + * Initially, ima_measure points to the default policy rules, now + * point to the new policy rules, and remove the securityfs policy file. + */ +static int ima_release_policy(struct inode *inode, struct file *file) +{ + if (!valid_policy) { + ima_delete_rules(); + return 0; + } + ima_update_policy(); + securityfs_remove(ima_policy); + ima_policy = NULL; + return 0; +} + +static struct file_operations ima_measure_policy_ops = { + .write = ima_write_policy, + .release = ima_release_policy +}; int ima_fs_init(void) { @@ -276,13 +333,20 @@ int ima_fs_init(void) if (IS_ERR(violations)) goto out; - return 0; + ima_policy = securityfs_create_file("policy", + S_IRUSR | S_IRGRP | S_IWUSR, + ima_dir, NULL, + &ima_measure_policy_ops); + if (IS_ERR(ima_policy)) + goto out; + return 0; out: securityfs_remove(runtime_measurements_count); securityfs_remove(ascii_runtime_measurements); securityfs_remove(binary_runtime_measurements); securityfs_remove(ima_dir); + securityfs_remove(ima_policy); return -1; } @@ -293,4 +357,5 @@ void __exit ima_fs_cleanup(void) securityfs_remove(ascii_runtime_measurements); securityfs_remove(binary_runtime_measurements); securityfs_remove(ima_dir); + securityfs_remove(ima_policy); } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 7c3d1ffb1472..bd453603e2c3 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "ima.h" @@ -24,7 +25,12 @@ #define IMA_FSMAGIC 0x0004 #define IMA_UID 0x0008 -enum ima_action { DONT_MEASURE, MEASURE }; +enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE }; + +#define MAX_LSM_RULES 6 +enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, + LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE +}; struct ima_measure_rule_entry { struct list_head list; @@ -34,8 +40,15 @@ struct ima_measure_rule_entry { int mask; unsigned long fsmagic; uid_t uid; + struct { + void *rule; /* LSM file metadata specific */ + int type; /* audit type */ + } lsm[MAX_LSM_RULES]; }; +/* Without LSM specific knowledge, the default policy can only be + * written in terms of .action, .func, .mask, .fsmagic, and .uid + */ static struct ima_measure_rule_entry default_rules[] = { {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, @@ -54,8 +67,11 @@ static struct ima_measure_rule_entry default_rules[] = { }; static LIST_HEAD(measure_default_rules); +static LIST_HEAD(measure_policy_rules); static struct list_head *ima_measure; +static DEFINE_MUTEX(ima_measure_mutex); + /** * ima_match_rules - determine whether an inode matches the measure rule. * @rule: a pointer to a rule @@ -69,6 +85,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, struct inode *inode, enum ima_hooks func, int mask) { struct task_struct *tsk = current; + int i; if ((rule->flags & IMA_FUNC) && rule->func != func) return false; @@ -79,6 +96,39 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, return false; if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid) return false; + for (i = 0; i < MAX_LSM_RULES; i++) { + int rc; + u32 osid, sid; + + if (!rule->lsm[i].rule) + continue; + + switch (i) { + case LSM_OBJ_USER: + case LSM_OBJ_ROLE: + case LSM_OBJ_TYPE: + security_inode_getsecid(inode, &osid); + rc = security_filter_rule_match(osid, + rule->lsm[i].type, + AUDIT_EQUAL, + rule->lsm[i].rule, + NULL); + break; + case LSM_SUBJ_USER: + case LSM_SUBJ_ROLE: + case LSM_SUBJ_TYPE: + security_task_getsecid(tsk, &sid); + rc = security_filter_rule_match(sid, + rule->lsm[i].type, + AUDIT_EQUAL, + rule->lsm[i].rule, + NULL); + default: + break; + } + if (!rc) + return false; + } return true; } @@ -112,9 +162,8 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask) /** * ima_init_policy - initialize the default measure rules. * - * (Could use the default_rules directly, but in policy patch * ima_measure points to either the measure_default_rules or the - * the new measure_policy_rules.) + * the new measure_policy_rules. */ void ima_init_policy(void) { @@ -124,3 +173,241 @@ void ima_init_policy(void) list_add_tail(&default_rules[i].list, &measure_default_rules); ima_measure = &measure_default_rules; } + +/** + * ima_update_policy - update default_rules with new measure rules + * + * Called on file .release to update the default rules with a complete new + * policy. Once updated, the policy is locked, no additional rules can be + * added to the policy. + */ +void ima_update_policy(void) +{ + const char *op = "policy_update"; + const char *cause = "already exists"; + int result = 1; + int audit_info = 0; + + if (ima_measure == &measure_default_rules) { + ima_measure = &measure_policy_rules; + cause = "complete"; + result = 0; + } + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, + NULL, op, cause, result, audit_info); +} + +enum { + Opt_err = -1, + Opt_measure = 1, Opt_dont_measure, + Opt_obj_user, Opt_obj_role, Opt_obj_type, + Opt_subj_user, Opt_subj_role, Opt_subj_type, + Opt_func, Opt_mask, Opt_fsmagic, Opt_uid +}; + +static match_table_t policy_tokens = { + {Opt_measure, "measure"}, + {Opt_dont_measure, "dont_measure"}, + {Opt_obj_user, "obj_user=%s"}, + {Opt_obj_role, "obj_role=%s"}, + {Opt_obj_type, "obj_type=%s"}, + {Opt_subj_user, "subj_user=%s"}, + {Opt_subj_role, "subj_role=%s"}, + {Opt_subj_type, "subj_type=%s"}, + {Opt_func, "func=%s"}, + {Opt_mask, "mask=%s"}, + {Opt_fsmagic, "fsmagic=%s"}, + {Opt_uid, "uid=%s"}, + {Opt_err, NULL} +}; + +static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, + char *args, int lsm_rule, int audit_type) +{ + int result; + + entry->lsm[lsm_rule].type = audit_type; + result = security_filter_rule_init(entry->lsm[lsm_rule].type, + AUDIT_EQUAL, args, + &entry->lsm[lsm_rule].rule); + return result; +} + +static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) +{ + struct audit_buffer *ab; + char *p; + int result = 0; + + ab = audit_log_start(current->audit_context, GFP_KERNEL, + AUDIT_INTEGRITY_STATUS); + + entry->action = -1; + while ((p = strsep(&rule, " \n")) != NULL) { + substring_t args[MAX_OPT_ARGS]; + int token; + unsigned long lnum; + + if (result < 0) + break; + if (!*p) + continue; + token = match_token(p, policy_tokens, args); + switch (token) { + case Opt_measure: + audit_log_format(ab, "%s ", "measure"); + entry->action = MEASURE; + break; + case Opt_dont_measure: + audit_log_format(ab, "%s ", "dont_measure"); + entry->action = DONT_MEASURE; + break; + case Opt_func: + audit_log_format(ab, "func=%s ", args[0].from); + if (strcmp(args[0].from, "PATH_CHECK") == 0) + entry->func = PATH_CHECK; + else if (strcmp(args[0].from, "FILE_MMAP") == 0) + entry->func = FILE_MMAP; + else if (strcmp(args[0].from, "BPRM_CHECK") == 0) + entry->func = BPRM_CHECK; + else + result = -EINVAL; + if (!result) + entry->flags |= IMA_FUNC; + break; + case Opt_mask: + audit_log_format(ab, "mask=%s ", args[0].from); + if ((strcmp(args[0].from, "MAY_EXEC")) == 0) + entry->mask = MAY_EXEC; + else if (strcmp(args[0].from, "MAY_WRITE") == 0) + entry->mask = MAY_WRITE; + else if (strcmp(args[0].from, "MAY_READ") == 0) + entry->mask = MAY_READ; + else if (strcmp(args[0].from, "MAY_APPEND") == 0) + entry->mask = MAY_APPEND; + else + result = -EINVAL; + if (!result) + entry->flags |= IMA_MASK; + break; + case Opt_fsmagic: + audit_log_format(ab, "fsmagic=%s ", args[0].from); + result = strict_strtoul(args[0].from, 16, + &entry->fsmagic); + if (!result) + entry->flags |= IMA_FSMAGIC; + break; + case Opt_uid: + audit_log_format(ab, "uid=%s ", args[0].from); + result = strict_strtoul(args[0].from, 10, &lnum); + if (!result) { + entry->uid = (uid_t) lnum; + if (entry->uid != lnum) + result = -EINVAL; + else + entry->flags |= IMA_UID; + } + break; + case Opt_obj_user: + audit_log_format(ab, "obj_user=%s ", args[0].from); + result = ima_lsm_rule_init(entry, args[0].from, + LSM_OBJ_USER, + AUDIT_OBJ_USER); + break; + case Opt_obj_role: + audit_log_format(ab, "obj_role=%s ", args[0].from); + result = ima_lsm_rule_init(entry, args[0].from, + LSM_OBJ_ROLE, + AUDIT_OBJ_ROLE); + break; + case Opt_obj_type: + audit_log_format(ab, "obj_type=%s ", args[0].from); + result = ima_lsm_rule_init(entry, args[0].from, + LSM_OBJ_TYPE, + AUDIT_OBJ_TYPE); + break; + case Opt_subj_user: + audit_log_format(ab, "subj_user=%s ", args[0].from); + result = ima_lsm_rule_init(entry, args[0].from, + LSM_SUBJ_USER, + AUDIT_SUBJ_USER); + break; + case Opt_subj_role: + audit_log_format(ab, "subj_role=%s ", args[0].from); + result = ima_lsm_rule_init(entry, args[0].from, + LSM_SUBJ_ROLE, + AUDIT_SUBJ_ROLE); + break; + case Opt_subj_type: + audit_log_format(ab, "subj_type=%s ", args[0].from); + result = ima_lsm_rule_init(entry, args[0].from, + LSM_SUBJ_TYPE, + AUDIT_SUBJ_TYPE); + break; + case Opt_err: + printk(KERN_INFO "%s: unknown token: %s\n", + __FUNCTION__, p); + break; + } + } + if (entry->action == UNKNOWN) + result = -EINVAL; + + audit_log_format(ab, "res=%d", result); + audit_log_end(ab); + return result; +} + +/** + * ima_parse_add_rule - add a rule to measure_policy_rules + * @rule - ima measurement policy rule + * + * Uses a mutex to protect the policy list from multiple concurrent writers. + * Returns 0 on success, an error code on failure. + */ +int ima_parse_add_rule(char *rule) +{ + const char *op = "add_rule"; + struct ima_measure_rule_entry *entry; + int result = 0; + int audit_info = 0; + + /* Prevent installed policy from changing */ + if (ima_measure != &measure_default_rules) { + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, + NULL, op, "already exists", + -EACCES, audit_info); + return -EACCES; + } + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, + NULL, op, "-ENOMEM", -ENOMEM, audit_info); + return -ENOMEM; + } + + INIT_LIST_HEAD(&entry->list); + + result = ima_parse_rule(rule, entry); + if (!result) { + mutex_lock(&ima_measure_mutex); + list_add_tail(&entry->list, &measure_policy_rules); + mutex_unlock(&ima_measure_mutex); + } else + kfree(entry); + return result; +} + +/* ima_delete_rules called to cleanup invalid policy */ +void ima_delete_rules() +{ + struct ima_measure_rule_entry *entry, *tmp; + + mutex_lock(&ima_measure_mutex); + list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) { + list_del(&entry->list); + kfree(entry); + } + mutex_unlock(&ima_measure_mutex); +} -- cgit v1.2.3-59-g8ed1b From 34bcda616e5308a0633d5bfabcc090d7aa09b494 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 5 Feb 2009 22:04:47 +0300 Subject: powerpc: Document FSL eSDHC bindings This patch documents OF bindings for the Freescale Enhanced Secure Digital Host Controller. Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- Documentation/powerpc/dts-bindings/fsl/esdhc.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/powerpc/dts-bindings/fsl/esdhc.txt (limited to 'Documentation') diff --git a/Documentation/powerpc/dts-bindings/fsl/esdhc.txt b/Documentation/powerpc/dts-bindings/fsl/esdhc.txt new file mode 100644 index 000000000000..600846557763 --- /dev/null +++ b/Documentation/powerpc/dts-bindings/fsl/esdhc.txt @@ -0,0 +1,24 @@ +* Freescale Enhanced Secure Digital Host Controller (eSDHC) + +The Enhanced Secure Digital Host Controller provides an interface +for MMC, SD, and SDIO types of memory cards. + +Required properties: + - compatible : should be + "fsl,-esdhc", "fsl,mpc8379-esdhc" for MPC83xx processors. + "fsl,-esdhc", "fsl,mpc8536-esdhc" for MPC85xx processors. + - reg : should contain eSDHC registers location and length. + - interrupts : should contain eSDHC interrupt. + - interrupt-parent : interrupt source phandle. + - clock-frequency : specifies eSDHC base clock frequency. + +Example: + +sdhci@2e000 { + compatible = "fsl,mpc8378-esdhc", "fsl,mpc8379-esdhc"; + reg = <0x2e000 0x1000>; + interrupts = <42 0x8>; + interrupt-parent = <&ipic>; + /* Filled in by U-Boot */ + clock-frequency = <0>; +}; -- cgit v1.2.3-59-g8ed1b From ae374d667a54fb5e2c9c0c4e87b206bd665f3ad6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Feb 2009 08:33:55 +0100 Subject: ALSA: hda - Update documentation Update documentation regarding codec probing; the new probe_only option and the new probe_mask usage. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/ALSA-Configuration.txt | 3 +++ Documentation/sound/alsa/HD-Audio.txt | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 841a9365d5fd..012afd7afb13 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -757,6 +757,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. model - force the model name position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF) probe_mask - Bitmask to probe codecs (default = -1, meaning all slots) + When the bit 8 (0x100) is set, the lower 8 bits are used + as the "fixed" codec slots; i.e. the driver probes the + slots regardless what hardware reports back probe_only - Only probing and no codec initialization (default=off); Useful to check the initial codec status for debugging bdl_pos_adj - Specifies the DMA IRQ timing delay in samples. diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index 8d68fff71839..99f7fbbe3e67 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -109,6 +109,13 @@ slot, pass `probe_mask=1`. For the first and the third slots, pass Since 2.6.29 kernel, the driver has a more robust probing method, so this error might happen rarely, though. +On a machine with a broken BIOS, sometimes you need to force the +driver to probe the codec slots the hardware doesn't report for use. +In such a case, turn the bit 8 (0x100) of `probe_mask` option on. +Then the rest 8 bits are passed as the codec slots to probe +unconditionally. For example, `probe_mask=0x103` will force to probe +the codec slots 0 and 1 no matter what the hardware reports. + Interrupt Handling ~~~~~~~~~~~~~~~~~~ @@ -461,6 +468,16 @@ run with `--no-upload` option, and attach the generated file. There are some other useful options. See `--help` option output for details. +When a probe error occurs or when the driver obviously assigns a +mismatched model, it'd be helpful to load the driver with +`probe_only=1` option (at best after the cold reboot) and run +alsa-info at this state. With this option, the driver won't configure +the mixer and PCM but just tries to probe the codec slot. After +probing, the proc file is available, so you can get the raw codec +information before modified by the driver. Of course, the driver +isn't usable with `probe_only=1`. But you can continue the +configuration via hwdep sysfs file if hda-reconfig option is enabled. + hda-verb ~~~~~~~~ -- cgit v1.2.3-59-g8ed1b From 27e089888fb1a3d1d13892262f9d522b03985044 Mon Sep 17 00:00:00 2001 From: Aristeu Sergio Rozanski Filho Date: Thu, 12 Feb 2009 17:50:37 -0500 Subject: ALSA: hda: add quirk for Lenovo X200 laptop dock Currently the HP connector on X200 dock doesn't detect when a HP is connected nor allows sound to be played using it. This patch fixes the problem by adding a quirk for this specific model. It's possible that others have the same NID (0x19) to report when dock HP is connected, but I don't have access to any. Please Cc me in the reply since I'm not subscribed to alsa-devel@. Signed-off-by: Aristeu Rozanski Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 1 + sound/pci/hda/patch_conexant.c | 40 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 8f40999a456e..0e52d273ce96 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -262,6 +262,7 @@ Conexant 5051 ============= laptop Basic Laptop config (default) hp HP Spartan laptop + lenovo-x200 Lenovo X200 laptop STAC9200 ======== diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index fdf876be712d..b8de73ecfdeb 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1798,6 +1798,40 @@ static struct hda_verb cxt5051_init_verbs[] = { { } /* end */ }; +static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { + /* Line in, Mic */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + /* SPK */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP, Amp */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Docking HP */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Record selector: Int mic */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, + /* SPDIF route: PCM */ + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* EAPD */ + {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, + { } /* end */ +}; + /* initialize jack-sensing, too */ static int cxt5051_init(struct hda_codec *codec) { @@ -1815,18 +1849,21 @@ static int cxt5051_init(struct hda_codec *codec) enum { CXT5051_LAPTOP, /* Laptops w/ EAPD support */ CXT5051_HP, /* no docking */ + CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ CXT5051_MODELS }; static const char *cxt5051_models[CXT5051_MODELS] = { [CXT5051_LAPTOP] = "laptop", [CXT5051_HP] = "hp", + [CXT5051_LENOVO_X200] = "lenovo-x200", }; static struct snd_pci_quirk cxt5051_cfg_tbl[] = { SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), + SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), {} }; @@ -1867,6 +1904,9 @@ static int patch_cxt5051(struct hda_codec *codec) codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; spec->mixers[0] = cxt5051_hp_mixers; break; + case CXT5051_LENOVO_X200: + spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; + /* fallthru */ default: case CXT5051_LAPTOP: codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; -- cgit v1.2.3-59-g8ed1b From f510b233cfc7bfd57b6007071c52aa42e3d16b06 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 22 Jan 2009 17:53:47 +0100 Subject: lockdep: get_user_chars() redo Generic, states independent, get_user_chars(). Signed-off-by: Peter Zijlstra Signed-off-by: Ingo Molnar --- Documentation/lockdep-design.txt | 30 +++++++++++++++++------------- kernel/lockdep.c | 24 ++++++++++++------------ kernel/lockdep_internals.h | 7 ++++--- kernel/lockdep_proc.c | 6 +++--- 4 files changed, 36 insertions(+), 31 deletions(-) (limited to 'Documentation') diff --git a/Documentation/lockdep-design.txt b/Documentation/lockdep-design.txt index 488773018152..938ea22f2cc0 100644 --- a/Documentation/lockdep-design.txt +++ b/Documentation/lockdep-design.txt @@ -27,33 +27,37 @@ lock-class. State ----- -The validator tracks lock-class usage history into 5 separate state bits: +The validator tracks lock-class usage history into 4n + 1 separate state bits: -- 'ever held in hardirq context' [ == hardirq-safe ] -- 'ever held in softirq context' [ == softirq-safe ] -- 'ever held with hardirqs enabled' [ == hardirq-unsafe ] -- 'ever held with softirqs and hardirqs enabled' [ == softirq-unsafe ] +- 'ever held in STATE context' +- 'ever head as readlock in STATE context' +- 'ever head with STATE enabled' +- 'ever head as readlock with STATE enabled' + +Where STATE can be either one of (kernel/lockdep_states.h) + - hardirq + - softirq + - reclaim_fs - 'ever used' [ == !unused ] -When locking rules are violated, these 4 state bits are presented in the -locking error messages, inside curlies. A contrived example: +When locking rules are violated, these state bits are presented in the +locking error messages, inside curlies. A contrived example: modprobe/2287 is trying to acquire lock: - (&sio_locks[i].lock){--..}, at: [] mutex_lock+0x21/0x24 + (&sio_locks[i].lock){-.-...}, at: [] mutex_lock+0x21/0x24 but task is already holding lock: - (&sio_locks[i].lock){--..}, at: [] mutex_lock+0x21/0x24 + (&sio_locks[i].lock){-.-...}, at: [] mutex_lock+0x21/0x24 -The bit position indicates hardirq, softirq, hardirq-read, -softirq-read respectively, and the character displayed in each -indicates: +The bit position indicates STATE, STATE-read, for each of the states listed +above, and the character displayed in each indicates: '.' acquired while irqs disabled '+' acquired in irq context '-' acquired with irqs enabled - '?' read acquired in irq context with irqs enabled. + '?' acquired in irq context with irqs enabled. Unused mutexes cannot be part of the cause of an error. diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 1b4ee3c0b789..22ced8d4912f 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -487,25 +487,25 @@ static char get_usage_char(struct lock_class *class, enum lock_usage_bit bit) return c; } -void -get_usage_chars(struct lock_class *class, char *c1, char *c2, char *c3, - char *c4, char *c5, char *c6) +void get_usage_chars(struct lock_class *class, char usage[LOCK_USAGE_CHARS]) { - *c1 = get_usage_char(class, LOCK_USED_IN_HARDIRQ); - *c2 = get_usage_char(class, LOCK_USED_IN_SOFTITQ); - *c3 = get_usage_char(class, LOCK_USED_IN_HARDIRQ_READ); - *c4 = get_usage_char(class, LOCK_USED_IN_SOFTITQ_READ); + int i = 0; - *c5 = get_usage_char(class, LOCK_USED_IN_RECLAIM_FS); - *c6 = get_usage_char(class, LOCK_USED_IN_RECLAIM_FS_READ); +#define LOCKDEP_STATE(__STATE) \ + usage[i++] = get_usage_char(class, LOCK_USED_IN_##__STATE); \ + usage[i++] = get_usage_char(class, LOCK_USED_IN_##__STATE##_READ); +#include "lockdep_states.h" +#undef LOCKDEP_STATE + + usage[i] = '\0'; } static void print_lock_name(struct lock_class *class) { - char str[KSYM_NAME_LEN], c1, c2, c3, c4, c5, c6; + char str[KSYM_NAME_LEN], usage[LOCK_USAGE_CHARS]; const char *name; - get_usage_chars(class, &c1, &c2, &c3, &c4, &c5, &c6); + get_usage_chars(class, usage); name = class->name; if (!name) { @@ -518,7 +518,7 @@ static void print_lock_name(struct lock_class *class) if (class->subclass) printk("/%d", class->subclass); } - printk("){%c%c%c%c%c%c}", c1, c2, c3, c4, c5, c6); + printk("){%s}", usage); } static void print_lockdep_cache(struct lockdep_map *lock) diff --git a/kernel/lockdep_internals.h b/kernel/lockdep_internals.h index 7e653e66ce5a..a2cc7e9a6e84 100644 --- a/kernel/lockdep_internals.h +++ b/kernel/lockdep_internals.h @@ -70,9 +70,10 @@ enum { extern struct list_head all_lock_classes; extern struct lock_chain lock_chains[]; -extern void -get_usage_chars(struct lock_class *class, char *c1, char *c2, char *c3, - char *c4, char *c5, char *c6); +#define LOCK_USAGE_CHARS (1+LOCK_USAGE_STATES/2) + +extern void get_usage_chars(struct lock_class *class, + char usage[LOCK_USAGE_CHARS]); extern const char * __get_key_name(struct lockdep_subclass_key *key, char *str); diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c index bd474fd9df9d..b51064ce564a 100644 --- a/kernel/lockdep_proc.c +++ b/kernel/lockdep_proc.c @@ -84,7 +84,7 @@ static int l_show(struct seq_file *m, void *v) { struct lock_class *class = v; struct lock_list *entry; - char c1, c2, c3, c4, c5, c6; + char usage[LOCK_USAGE_CHARS]; if (v == SEQ_START_TOKEN) { seq_printf(m, "all lock classes:\n"); @@ -100,8 +100,8 @@ static int l_show(struct seq_file *m, void *v) seq_printf(m, " BD:%5ld", lockdep_count_backward_deps(class)); #endif - get_usage_chars(class, &c1, &c2, &c3, &c4, &c5, &c6); - seq_printf(m, " %c%c%c%c%c%c", c1, c2, c3, c4, c5, c6); + get_usage_chars(class, usage); + seq_printf(m, " %s", usage); seq_printf(m, ": "); print_name(m, class); -- cgit v1.2.3-59-g8ed1b From cb9eff097831007afb30d64373f29d99825d0068 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Feb 2009 05:03:36 +0000 Subject: net: new user space API for time stamping of incoming and outgoing packets User space can request hardware and/or software time stamping. Reporting of the result(s) via a new control message is enabled separately for each field in the message because some of the fields may require additional computation and thus cause overhead. User space can tell the different kinds of time stamps apart and choose what suits its needs. When a TX timestamp operation is requested, the TX skb will be cloned and the clone will be time stamped (in hardware or software) and added to the socket error queue of the skb, if the skb has a socket associated with it. The actual TX timestamp will reach userspace as a RX timestamp on the cloned packet. If timestamping is requested and no timestamping is done in the device driver (potentially this may use hardware timestamping), it will be done in software after the device's start_hard_xmit routine. Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- Documentation/networking/timestamping.txt | 178 +++++++ Documentation/networking/timestamping/.gitignore | 1 + Documentation/networking/timestamping/Makefile | 6 + .../networking/timestamping/timestamping.c | 533 +++++++++++++++++++++ arch/alpha/include/asm/socket.h | 3 + arch/arm/include/asm/socket.h | 3 + arch/avr32/include/asm/socket.h | 3 + arch/blackfin/include/asm/socket.h | 3 + arch/cris/include/asm/socket.h | 3 + arch/h8300/include/asm/socket.h | 3 + arch/ia64/include/asm/socket.h | 3 + arch/m68k/include/asm/socket.h | 3 + arch/mips/include/asm/socket.h | 3 + arch/parisc/include/asm/socket.h | 3 + arch/powerpc/include/asm/socket.h | 3 + arch/s390/include/asm/socket.h | 3 + arch/sh/include/asm/socket.h | 3 + arch/sparc/include/asm/socket.h | 3 + arch/x86/include/asm/socket.h | 3 + arch/xtensa/include/asm/socket.h | 3 + include/asm-frv/socket.h | 3 + include/asm-m32r/socket.h | 3 + include/asm-mn10300/socket.h | 3 + include/linux/errqueue.h | 1 + include/linux/net_tstamp.h | 104 ++++ include/linux/sockios.h | 3 + 26 files changed, 883 insertions(+) create mode 100644 Documentation/networking/timestamping.txt create mode 100644 Documentation/networking/timestamping/.gitignore create mode 100644 Documentation/networking/timestamping/Makefile create mode 100644 Documentation/networking/timestamping/timestamping.c create mode 100644 include/linux/net_tstamp.h (limited to 'Documentation') diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt new file mode 100644 index 000000000000..a681a65b5bc7 --- /dev/null +++ b/Documentation/networking/timestamping.txt @@ -0,0 +1,178 @@ +The existing interfaces for getting network packages time stamped are: + +* SO_TIMESTAMP + Generate time stamp for each incoming packet using the (not necessarily + monotonous!) system time. Result is returned via recv_msg() in a + control message as timeval (usec resolution). + +* SO_TIMESTAMPNS + Same time stamping mechanism as SO_TIMESTAMP, but returns result as + timespec (nsec resolution). + +* IP_MULTICAST_LOOP + SO_TIMESTAMP[NS] + Only for multicasts: approximate send time stamp by receiving the looped + packet and using its receive time stamp. + +The following interface complements the existing ones: receive time +stamps can be generated and returned for arbitrary packets and much +closer to the point where the packet is really sent. Time stamps can +be generated in software (as before) or in hardware (if the hardware +has such a feature). + +SO_TIMESTAMPING: + +Instructs the socket layer which kind of information is wanted. The +parameter is an integer with some of the following bits set. Setting +other bits is an error and doesn't change the current state. + +SOF_TIMESTAMPING_TX_HARDWARE: try to obtain send time stamp in hardware +SOF_TIMESTAMPING_TX_SOFTWARE: if SOF_TIMESTAMPING_TX_HARDWARE is off or + fails, then do it in software +SOF_TIMESTAMPING_RX_HARDWARE: return the original, unmodified time stamp + as generated by the hardware +SOF_TIMESTAMPING_RX_SOFTWARE: if SOF_TIMESTAMPING_RX_HARDWARE is off or + fails, then do it in software +SOF_TIMESTAMPING_RAW_HARDWARE: return original raw hardware time stamp +SOF_TIMESTAMPING_SYS_HARDWARE: return hardware time stamp transformed to + the system time base +SOF_TIMESTAMPING_SOFTWARE: return system time stamp generated in + software + +SOF_TIMESTAMPING_TX/RX determine how time stamps are generated. +SOF_TIMESTAMPING_RAW/SYS determine how they are reported in the +following control message: + struct scm_timestamping { + struct timespec systime; + struct timespec hwtimetrans; + struct timespec hwtimeraw; + }; + +recvmsg() can be used to get this control message for regular incoming +packets. For send time stamps the outgoing packet is looped back to +the socket's error queue with the send time stamp(s) attached. It can +be received with recvmsg(flags=MSG_ERRQUEUE). The call returns the +original outgoing packet data including all headers preprended down to +and including the link layer, the scm_timestamping control message and +a sock_extended_err control message with ee_errno==ENOMSG and +ee_origin==SO_EE_ORIGIN_TIMESTAMPING. A socket with such a pending +bounced packet is ready for reading as far as select() is concerned. + +All three values correspond to the same event in time, but were +generated in different ways. Each of these values may be empty (= all +zero), in which case no such value was available. If the application +is not interested in some of these values, they can be left blank to +avoid the potential overhead of calculating them. + +systime is the value of the system time at that moment. This +corresponds to the value also returned via SO_TIMESTAMP[NS]. If the +time stamp was generated by hardware, then this field is +empty. Otherwise it is filled in if SOF_TIMESTAMPING_SOFTWARE is +set. + +hwtimeraw is the original hardware time stamp. Filled in if +SOF_TIMESTAMPING_RAW_HARDWARE is set. No assumptions about its +relation to system time should be made. + +hwtimetrans is the hardware time stamp transformed so that it +corresponds as good as possible to system time. This correlation is +not perfect; as a consequence, sorting packets received via different +NICs by their hwtimetrans may differ from the order in which they were +received. hwtimetrans may be non-monotonic even for the same NIC. +Filled in if SOF_TIMESTAMPING_SYS_HARDWARE is set. Requires support +by the network device and will be empty without that support. + + +SIOCSHWTSTAMP: + +Hardware time stamping must also be initialized for each device driver +that is expected to do hardware time stamping. The parameter is: + +struct hwtstamp_config { + int flags; /* no flags defined right now, must be zero */ + int tx_type; /* HWTSTAMP_TX_* */ + int rx_filter; /* HWTSTAMP_FILTER_* */ +}; + +Desired behavior is passed into the kernel and to a specific device by +calling ioctl(SIOCSHWTSTAMP) with a pointer to a struct ifreq whose +ifr_data points to a struct hwtstamp_config. The tx_type and +rx_filter are hints to the driver what it is expected to do. If +the requested fine-grained filtering for incoming packets is not +supported, the driver may time stamp more than just the requested types +of packets. + +A driver which supports hardware time stamping shall update the struct +with the actual, possibly more permissive configuration. If the +requested packets cannot be time stamped, then nothing should be +changed and ERANGE shall be returned (in contrast to EINVAL, which +indicates that SIOCSHWTSTAMP is not supported at all). + +Only a processes with admin rights may change the configuration. User +space is responsible to ensure that multiple processes don't interfere +with each other and that the settings are reset. + +/* possible values for hwtstamp_config->tx_type */ +enum { + /* + * no outgoing packet will need hardware time stamping; + * should a packet arrive which asks for it, no hardware + * time stamping will be done + */ + HWTSTAMP_TX_OFF, + + /* + * enables hardware time stamping for outgoing packets; + * the sender of the packet decides which are to be + * time stamped by setting SOF_TIMESTAMPING_TX_SOFTWARE + * before sending the packet + */ + HWTSTAMP_TX_ON, +}; + +/* possible values for hwtstamp_config->rx_filter */ +enum { + /* time stamp no incoming packet at all */ + HWTSTAMP_FILTER_NONE, + + /* time stamp any incoming packet */ + HWTSTAMP_FILTER_ALL, + + /* return value: time stamp all packets requested plus some others */ + HWTSTAMP_FILTER_SOME, + + /* PTP v1, UDP, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V1_L4_EVENT, + + ... +}; + + +DEVICE IMPLEMENTATION + +A driver which supports hardware time stamping must support the +SIOCSHWTSTAMP ioctl. Time stamps for received packets must be stored +in the skb with skb_hwtstamp_set(). + +Time stamps for outgoing packets are to be generated as follows: +- In hard_start_xmit(), check if skb_hwtstamp_check_tx_hardware() + returns non-zero. If yes, then the driver is expected + to do hardware time stamping. +- If this is possible for the skb and requested, then declare + that the driver is doing the time stamping by calling + skb_hwtstamp_tx_in_progress(). A driver not supporting + hardware time stamping doesn't do that. A driver must never + touch sk_buff::tstamp! It is used to store how time stamping + for an outgoing packets is to be done. +- As soon as the driver has sent the packet and/or obtained a + hardware time stamp for it, it passes the time stamp back by + calling skb_hwtstamp_tx() with the original skb, the raw + hardware time stamp and a handle to the device (necessary + to convert the hardware time stamp to system time). If obtaining + the hardware time stamp somehow fails, then the driver should + not fall back to software time stamping. The rationale is that + this would occur at a later time in the processing pipeline + than other software time stamping and therefore could lead + to unexpected deltas between time stamps. +- If the driver did not call skb_hwtstamp_tx_in_progress(), then + dev_hard_start_xmit() checks whether software time stamping + is wanted as fallback and potentially generates the time stamp. diff --git a/Documentation/networking/timestamping/.gitignore b/Documentation/networking/timestamping/.gitignore new file mode 100644 index 000000000000..71e81eb2e22f --- /dev/null +++ b/Documentation/networking/timestamping/.gitignore @@ -0,0 +1 @@ +timestamping diff --git a/Documentation/networking/timestamping/Makefile b/Documentation/networking/timestamping/Makefile new file mode 100644 index 000000000000..2a1489fdc036 --- /dev/null +++ b/Documentation/networking/timestamping/Makefile @@ -0,0 +1,6 @@ +CPPFLAGS = -I../../../include + +timestamping: timestamping.c + +clean: + rm -f timestamping diff --git a/Documentation/networking/timestamping/timestamping.c b/Documentation/networking/timestamping/timestamping.c new file mode 100644 index 000000000000..43d143104210 --- /dev/null +++ b/Documentation/networking/timestamping/timestamping.c @@ -0,0 +1,533 @@ +/* + * This program demonstrates how the various time stamping features in + * the Linux kernel work. It emulates the behavior of a PTP + * implementation in stand-alone master mode by sending PTPv1 Sync + * multicasts once every second. It looks for similar packets, but + * beyond that doesn't actually implement PTP. + * + * Outgoing packets are time stamped with SO_TIMESTAMPING with or + * without hardware support. + * + * Incoming packets are time stamped with SO_TIMESTAMPING with or + * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and + * SO_TIMESTAMP[NS]. + * + * Copyright (C) 2009 Intel Corporation. + * Author: Patrick Ohly + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "asm/types.h" +#include "linux/net_tstamp.h" +#include "linux/errqueue.h" + +#ifndef SO_TIMESTAMPING +# define SO_TIMESTAMPING 37 +# define SCM_TIMESTAMPING SO_TIMESTAMPING +#endif + +#ifndef SO_TIMESTAMPNS +# define SO_TIMESTAMPNS 35 +#endif + +#ifndef SIOCGSTAMPNS +# define SIOCGSTAMPNS 0x8907 +#endif + +#ifndef SIOCSHWTSTAMP +# define SIOCSHWTSTAMP 0x89b0 +#endif + +static void usage(const char *error) +{ + if (error) + printf("invalid option: %s\n", error); + printf("timestamping interface option*\n\n" + "Options:\n" + " IP_MULTICAST_LOOP - looping outgoing multicasts\n" + " SO_TIMESTAMP - normal software time stamping, ms resolution\n" + " SO_TIMESTAMPNS - more accurate software time stamping\n" + " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n" + " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n" + " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n" + " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n" + " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n" + " SOF_TIMESTAMPING_SYS_HARDWARE - request reporting of transformed HW time stamps\n" + " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n" + " SIOCGSTAMP - check last socket time stamp\n" + " SIOCGSTAMPNS - more accurate socket time stamp\n"); + exit(1); +} + +static void bail(const char *error) +{ + printf("%s: %s\n", error, strerror(errno)); + exit(1); +} + +static const unsigned char sync[] = { + 0x00, 0x01, 0x00, 0x01, + 0x5f, 0x44, 0x46, 0x4c, + 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, + + /* fake uuid */ + 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + + 0x00, 0x01, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x49, 0x05, 0xcd, 0x01, + 0x29, 0xb1, 0x8d, 0xb0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + + /* fake uuid */ + 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + + 0x00, 0x00, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x04, + 0x44, 0x46, 0x4c, 0x54, + 0x00, 0x00, 0xf0, 0x60, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0xf0, 0x60, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, + 0x44, 0x46, 0x4c, 0x54, + 0x00, 0x01, + + /* fake uuid */ + 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len) +{ + struct timeval now; + int res; + + res = sendto(sock, sync, sizeof(sync), 0, + addr, addr_len); + gettimeofday(&now, 0); + if (res < 0) + printf("%s: %s\n", "send", strerror(errno)); + else + printf("%ld.%06ld: sent %d bytes\n", + (long)now.tv_sec, (long)now.tv_usec, + res); +} + +static void printpacket(struct msghdr *msg, int res, + char *data, + int sock, int recvmsg_flags, + int siocgstamp, int siocgstampns) +{ + struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; + struct cmsghdr *cmsg; + struct timeval tv; + struct timespec ts; + struct timeval now; + + gettimeofday(&now, 0); + + printf("%ld.%06ld: received %s data, %d bytes from %s, %d bytes control messages\n", + (long)now.tv_sec, (long)now.tv_usec, + (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", + res, + inet_ntoa(from_addr->sin_addr), + msg->msg_controllen); + for (cmsg = CMSG_FIRSTHDR(msg); + cmsg; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + printf(" cmsg len %d: ", cmsg->cmsg_len); + switch (cmsg->cmsg_level) { + case SOL_SOCKET: + printf("SOL_SOCKET "); + switch (cmsg->cmsg_type) { + case SO_TIMESTAMP: { + struct timeval *stamp = + (struct timeval *)CMSG_DATA(cmsg); + printf("SO_TIMESTAMP %ld.%06ld", + (long)stamp->tv_sec, + (long)stamp->tv_usec); + break; + } + case SO_TIMESTAMPNS: { + struct timespec *stamp = + (struct timespec *)CMSG_DATA(cmsg); + printf("SO_TIMESTAMPNS %ld.%09ld", + (long)stamp->tv_sec, + (long)stamp->tv_nsec); + break; + } + case SO_TIMESTAMPING: { + struct timespec *stamp = + (struct timespec *)CMSG_DATA(cmsg); + printf("SO_TIMESTAMPING "); + printf("SW %ld.%09ld ", + (long)stamp->tv_sec, + (long)stamp->tv_nsec); + stamp++; + printf("HW transformed %ld.%09ld ", + (long)stamp->tv_sec, + (long)stamp->tv_nsec); + stamp++; + printf("HW raw %ld.%09ld", + (long)stamp->tv_sec, + (long)stamp->tv_nsec); + break; + } + default: + printf("type %d", cmsg->cmsg_type); + break; + } + break; + case IPPROTO_IP: + printf("IPPROTO_IP "); + switch (cmsg->cmsg_type) { + case IP_RECVERR: { + struct sock_extended_err *err = + (struct sock_extended_err *)CMSG_DATA(cmsg); + printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s", + strerror(err->ee_errno), + err->ee_origin, +#ifdef SO_EE_ORIGIN_TIMESTAMPING + err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ? + "bounced packet" : "unexpected origin" +#else + "probably SO_EE_ORIGIN_TIMESTAMPING" +#endif + ); + if (res < sizeof(sync)) + printf(" => truncated data?!"); + else if (!memcmp(sync, data + res - sizeof(sync), + sizeof(sync))) + printf(" => GOT OUR DATA BACK (HURRAY!)"); + break; + } + case IP_PKTINFO: { + struct in_pktinfo *pktinfo = + (struct in_pktinfo *)CMSG_DATA(cmsg); + printf("IP_PKTINFO interface index %u", + pktinfo->ipi_ifindex); + break; + } + default: + printf("type %d", cmsg->cmsg_type); + break; + } + break; + default: + printf("level %d type %d", + cmsg->cmsg_level, + cmsg->cmsg_type); + break; + } + printf("\n"); + } + + if (siocgstamp) { + if (ioctl(sock, SIOCGSTAMP, &tv)) + printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno)); + else + printf("SIOCGSTAMP %ld.%06ld\n", + (long)tv.tv_sec, + (long)tv.tv_usec); + } + if (siocgstampns) { + if (ioctl(sock, SIOCGSTAMPNS, &ts)) + printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno)); + else + printf("SIOCGSTAMPNS %ld.%09ld\n", + (long)ts.tv_sec, + (long)ts.tv_nsec); + } +} + +static void recvpacket(int sock, int recvmsg_flags, + int siocgstamp, int siocgstampns) +{ + char data[256]; + struct msghdr msg; + struct iovec entry; + struct sockaddr_in from_addr; + struct { + struct cmsghdr cm; + char control[512]; + } control; + int res; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + entry.iov_base = data; + entry.iov_len = sizeof(data); + msg.msg_name = (caddr_t)&from_addr; + msg.msg_namelen = sizeof(from_addr); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT); + if (res < 0) { + printf("%s %s: %s\n", + "recvmsg", + (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", + strerror(errno)); + } else { + printpacket(&msg, res, data, + sock, recvmsg_flags, + siocgstamp, siocgstampns); + } +} + +int main(int argc, char **argv) +{ + int so_timestamping_flags = 0; + int so_timestamp = 0; + int so_timestampns = 0; + int siocgstamp = 0; + int siocgstampns = 0; + int ip_multicast_loop = 0; + char *interface; + int i; + int enabled = 1; + int sock; + struct ifreq device; + struct ifreq hwtstamp; + struct hwtstamp_config hwconfig, hwconfig_requested; + struct sockaddr_in addr; + struct ip_mreq imr; + struct in_addr iaddr; + int val; + socklen_t len; + struct timeval next; + + if (argc < 2) + usage(0); + interface = argv[1]; + + for (i = 2; i < argc; i++) { + if (!strcasecmp(argv[i], "SO_TIMESTAMP")) + so_timestamp = 1; + else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS")) + so_timestampns = 1; + else if (!strcasecmp(argv[i], "SIOCGSTAMP")) + siocgstamp = 1; + else if (!strcasecmp(argv[i], "SIOCGSTAMPNS")) + siocgstampns = 1; + else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP")) + ip_multicast_loop = 1; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SYS_HARDWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_SYS_HARDWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; + else + usage(argv[i]); + } + + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (socket < 0) + bail("socket"); + + memset(&device, 0, sizeof(device)); + strncpy(device.ifr_name, interface, sizeof(device.ifr_name)); + if (ioctl(sock, SIOCGIFADDR, &device) < 0) + bail("getting interface IP address"); + + memset(&hwtstamp, 0, sizeof(hwtstamp)); + strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name)); + hwtstamp.ifr_data = (void *)&hwconfig; + memset(&hwconfig, 0, sizeof(&hwconfig)); + hwconfig.tx_type = + (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ? + HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + hwconfig.rx_filter = + (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ? + HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE; + hwconfig_requested = hwconfig; + if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) { + if ((errno == EINVAL || errno == ENOTSUP) && + hwconfig_requested.tx_type == HWTSTAMP_TX_OFF && + hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE) + printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n"); + else + bail("SIOCSHWTSTAMP"); + } + printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n", + hwconfig_requested.tx_type, hwconfig.tx_type, + hwconfig_requested.rx_filter, hwconfig.rx_filter); + + /* bind to PTP port */ + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(319 /* PTP event port */); + if (bind(sock, + (struct sockaddr *)&addr, + sizeof(struct sockaddr_in)) < 0) + bail("bind"); + + /* set multicast group for outgoing packets */ + inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */ + addr.sin_addr = iaddr; + imr.imr_multiaddr.s_addr = iaddr.s_addr; + imr.imr_interface.s_addr = + ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr; + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0) + bail("set multicast"); + + /* join multicast group, loop our own packet */ + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &imr, sizeof(struct ip_mreq)) < 0) + bail("join multicast group"); + + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, + &ip_multicast_loop, sizeof(enabled)) < 0) { + bail("loop multicast"); + } + + /* set socket options for time stamping */ + if (so_timestamp && + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, + &enabled, sizeof(enabled)) < 0) + bail("setsockopt SO_TIMESTAMP"); + + if (so_timestampns && + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, + &enabled, sizeof(enabled)) < 0) + bail("setsockopt SO_TIMESTAMPNS"); + + if (so_timestamping_flags && + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, + &so_timestamping_flags, + sizeof(so_timestamping_flags)) < 0) + bail("setsockopt SO_TIMESTAMPING"); + + /* request IP_PKTINFO for debugging purposes */ + if (setsockopt(sock, SOL_IP, IP_PKTINFO, + &enabled, sizeof(enabled)) < 0) + printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno)); + + /* verify socket options */ + len = sizeof(val); + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0) + printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno)); + else + printf("SO_TIMESTAMP %d\n", val); + + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0) + printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS", + strerror(errno)); + else + printf("SO_TIMESTAMPNS %d\n", val); + + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) { + printf("%s: %s\n", "getsockopt SO_TIMESTAMPING", + strerror(errno)); + } else { + printf("SO_TIMESTAMPING %d\n", val); + if (val != so_timestamping_flags) + printf(" not the expected value %d\n", + so_timestamping_flags); + } + + /* send packets forever every five seconds */ + gettimeofday(&next, 0); + next.tv_sec = (next.tv_sec + 1) / 5 * 5; + next.tv_usec = 0; + while (1) { + struct timeval now; + struct timeval delta; + long delta_us; + int res; + fd_set readfs, errorfs; + + gettimeofday(&now, 0); + delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 + + (long)(next.tv_usec - now.tv_usec); + if (delta_us > 0) { + /* continue waiting for timeout or data */ + delta.tv_sec = delta_us / 1000000; + delta.tv_usec = delta_us % 1000000; + + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + FD_SET(sock, &readfs); + FD_SET(sock, &errorfs); + printf("%ld.%06ld: select %ldus\n", + (long)now.tv_sec, (long)now.tv_usec, + delta_us); + res = select(sock + 1, &readfs, 0, &errorfs, &delta); + gettimeofday(&now, 0); + printf("%ld.%06ld: select returned: %d, %s\n", + (long)now.tv_sec, (long)now.tv_usec, + res, + res < 0 ? strerror(errno) : "success"); + if (res > 0) { + if (FD_ISSET(sock, &readfs)) + printf("ready for reading\n"); + if (FD_ISSET(sock, &errorfs)) + printf("has error\n"); + recvpacket(sock, 0, + siocgstamp, + siocgstampns); + recvpacket(sock, MSG_ERRQUEUE, + siocgstamp, + siocgstampns); + } + } else { + /* write one packet */ + sendpacket(sock, + (struct sockaddr *)&addr, + sizeof(addr)); + next.tv_sec += 5; + continue; + } + } + + return 0; +} diff --git a/arch/alpha/include/asm/socket.h b/arch/alpha/include/asm/socket.h index a1057c2d95e7..3641ec1452f4 100644 --- a/arch/alpha/include/asm/socket.h +++ b/arch/alpha/include/asm/socket.h @@ -62,6 +62,9 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/arm/include/asm/socket.h b/arch/arm/include/asm/socket.h index 6817be9573a6..537de4e0ef50 100644 --- a/arch/arm/include/asm/socket.h +++ b/arch/arm/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/avr32/include/asm/socket.h b/arch/avr32/include/asm/socket.h index 35863f260929..04c860619700 100644 --- a/arch/avr32/include/asm/socket.h +++ b/arch/avr32/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/arch/blackfin/include/asm/socket.h b/arch/blackfin/include/asm/socket.h index 2ca702e44d47..fac7fe9e1f8a 100644 --- a/arch/blackfin/include/asm/socket.h +++ b/arch/blackfin/include/asm/socket.h @@ -53,4 +53,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/cris/include/asm/socket.h b/arch/cris/include/asm/socket.h index 9df0ca82f5de..d5cf74005408 100644 --- a/arch/cris/include/asm/socket.h +++ b/arch/cris/include/asm/socket.h @@ -56,6 +56,9 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/h8300/include/asm/socket.h b/arch/h8300/include/asm/socket.h index da2520dbf254..602518a70a1a 100644 --- a/arch/h8300/include/asm/socket.h +++ b/arch/h8300/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/asm/socket.h b/arch/ia64/include/asm/socket.h index d5ef0aa3e312..745421225ec6 100644 --- a/arch/ia64/include/asm/socket.h +++ b/arch/ia64/include/asm/socket.h @@ -63,4 +63,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m68k/include/asm/socket.h b/arch/m68k/include/asm/socket.h index dbc64e92c41a..ca87f938b03f 100644 --- a/arch/m68k/include/asm/socket.h +++ b/arch/m68k/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/mips/include/asm/socket.h b/arch/mips/include/asm/socket.h index facc2d7a87ca..2abca1780169 100644 --- a/arch/mips/include/asm/socket.h +++ b/arch/mips/include/asm/socket.h @@ -75,6 +75,9 @@ To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #ifdef __KERNEL__ /** sock_type - Socket types diff --git a/arch/parisc/include/asm/socket.h b/arch/parisc/include/asm/socket.h index fba402c95ac2..885472bf7b78 100644 --- a/arch/parisc/include/asm/socket.h +++ b/arch/parisc/include/asm/socket.h @@ -54,6 +54,9 @@ #define SO_MARK 0x401f +#define SO_TIMESTAMPING 0x4020 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/powerpc/include/asm/socket.h b/arch/powerpc/include/asm/socket.h index f5a4e168e498..1e5cfad0e3f7 100644 --- a/arch/powerpc/include/asm/socket.h +++ b/arch/powerpc/include/asm/socket.h @@ -61,4 +61,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/asm/socket.h b/arch/s390/include/asm/socket.h index c786ab623b2d..02330c50241b 100644 --- a/arch/s390/include/asm/socket.h +++ b/arch/s390/include/asm/socket.h @@ -62,4 +62,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sh/include/asm/socket.h b/arch/sh/include/asm/socket.h index 6d4bf6512959..345653b96826 100644 --- a/arch/sh/include/asm/socket.h +++ b/arch/sh/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* __ASM_SH_SOCKET_H */ diff --git a/arch/sparc/include/asm/socket.h b/arch/sparc/include/asm/socket.h index bf50d0c2d583..982a12f959f4 100644 --- a/arch/sparc/include/asm/socket.h +++ b/arch/sparc/include/asm/socket.h @@ -50,6 +50,9 @@ #define SO_MARK 0x0022 +#define SO_TIMESTAMPING 0x0023 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/x86/include/asm/socket.h b/arch/x86/include/asm/socket.h index 8ab9cc8b2ecc..ca8bf2cd0ba9 100644 --- a/arch/x86/include/asm/socket.h +++ b/arch/x86/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_X86_SOCKET_H */ diff --git a/arch/xtensa/include/asm/socket.h b/arch/xtensa/include/asm/socket.h index 6100682b1da2..dd1a7a4a1cea 100644 --- a/arch/xtensa/include/asm/socket.h +++ b/arch/xtensa/include/asm/socket.h @@ -65,4 +65,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/asm-frv/socket.h b/include/asm-frv/socket.h index e51ca67b9356..57c3d4054e8b 100644 --- a/include/asm-frv/socket.h +++ b/include/asm-frv/socket.h @@ -54,5 +54,8 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-m32r/socket.h b/include/asm-m32r/socket.h index 9a0e20012224..be7ed589af5c 100644 --- a/include/asm-m32r/socket.h +++ b/include/asm-m32r/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/include/asm-mn10300/socket.h b/include/asm-mn10300/socket.h index 80af9c4ccad7..fb5daf438ec9 100644 --- a/include/asm-mn10300/socket.h +++ b/include/asm-mn10300/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/include/linux/errqueue.h b/include/linux/errqueue.h index ceb1454b6977..ec12cc74366f 100644 --- a/include/linux/errqueue.h +++ b/include/linux/errqueue.h @@ -18,6 +18,7 @@ struct sock_extended_err #define SO_EE_ORIGIN_LOCAL 1 #define SO_EE_ORIGIN_ICMP 2 #define SO_EE_ORIGIN_ICMP6 3 +#define SO_EE_ORIGIN_TIMESTAMPING 4 #define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) diff --git a/include/linux/net_tstamp.h b/include/linux/net_tstamp.h new file mode 100644 index 000000000000..a3b8546354ac --- /dev/null +++ b/include/linux/net_tstamp.h @@ -0,0 +1,104 @@ +/* + * Userspace API for hardware time stamping of network packets + * + * Copyright (C) 2008,2009 Intel Corporation + * Author: Patrick Ohly + * + */ + +#ifndef _NET_TIMESTAMPING_H +#define _NET_TIMESTAMPING_H + +#include /* for SO_TIMESTAMPING */ + +/* SO_TIMESTAMPING gets an integer bit field comprised of these values */ +enum { + SOF_TIMESTAMPING_TX_HARDWARE = (1<<0), + SOF_TIMESTAMPING_TX_SOFTWARE = (1<<1), + SOF_TIMESTAMPING_RX_HARDWARE = (1<<2), + SOF_TIMESTAMPING_RX_SOFTWARE = (1<<3), + SOF_TIMESTAMPING_SOFTWARE = (1<<4), + SOF_TIMESTAMPING_SYS_HARDWARE = (1<<5), + SOF_TIMESTAMPING_RAW_HARDWARE = (1<<6), + SOF_TIMESTAMPING_MASK = + (SOF_TIMESTAMPING_RAW_HARDWARE - 1) | + SOF_TIMESTAMPING_RAW_HARDWARE +}; + +/** + * struct hwtstamp_config - %SIOCSHWTSTAMP parameter + * + * @flags: no flags defined right now, must be zero + * @tx_type: one of HWTSTAMP_TX_* + * @rx_type: one of one of HWTSTAMP_FILTER_* + * + * %SIOCSHWTSTAMP expects a &struct ifreq with a ifr_data pointer to + * this structure. dev_ifsioc() in the kernel takes care of the + * translation between 32 bit userspace and 64 bit kernel. The + * structure is intentionally chosen so that it has the same layout on + * 32 and 64 bit systems, don't break this! + */ +struct hwtstamp_config { + int flags; + int tx_type; + int rx_filter; +}; + +/* possible values for hwtstamp_config->tx_type */ +enum { + /* + * No outgoing packet will need hardware time stamping; + * should a packet arrive which asks for it, no hardware + * time stamping will be done. + */ + HWTSTAMP_TX_OFF, + + /* + * Enables hardware time stamping for outgoing packets; + * the sender of the packet decides which are to be + * time stamped by setting %SOF_TIMESTAMPING_TX_SOFTWARE + * before sending the packet. + */ + HWTSTAMP_TX_ON, +}; + +/* possible values for hwtstamp_config->rx_filter */ +enum { + /* time stamp no incoming packet at all */ + HWTSTAMP_FILTER_NONE, + + /* time stamp any incoming packet */ + HWTSTAMP_FILTER_ALL, + + /* return value: time stamp all packets requested plus some others */ + HWTSTAMP_FILTER_SOME, + + /* PTP v1, UDP, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V1_L4_EVENT, + /* PTP v1, UDP, Sync packet */ + HWTSTAMP_FILTER_PTP_V1_L4_SYNC, + /* PTP v1, UDP, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ, + /* PTP v2, UDP, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_L4_EVENT, + /* PTP v2, UDP, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_L4_SYNC, + /* PTP v2, UDP, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ, + + /* 802.AS1, Ethernet, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_L2_EVENT, + /* 802.AS1, Ethernet, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_L2_SYNC, + /* 802.AS1, Ethernet, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ, + + /* PTP v2/802.AS1, any layer, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_EVENT, + /* PTP v2/802.AS1, any layer, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_SYNC, + /* PTP v2/802.AS1, any layer, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_DELAY_REQ, +}; + +#endif /* _NET_TIMESTAMPING_H */ diff --git a/include/linux/sockios.h b/include/linux/sockios.h index abef7596655a..241f179347d9 100644 --- a/include/linux/sockios.h +++ b/include/linux/sockios.h @@ -122,6 +122,9 @@ #define SIOCBRADDIF 0x89a2 /* add interface to bridge */ #define SIOCBRDELIF 0x89a3 /* remove interface from bridge */ +/* hardware time stamping: parameters in linux/net_tstamp.h */ +#define SIOCSHWTSTAMP 0x89b0 + /* Device private ioctl calls */ /* -- cgit v1.2.3-59-g8ed1b From 51f31cabe3ce5345b51e4a4f82138b38c4d5dc91 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Feb 2009 05:03:39 +0000 Subject: ip: support for TX timestamps on UDP and RAW sockets Instructions for time stamping outgoing packets are take from the socket layer and later copied into the new skb. Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- Documentation/networking/timestamping.txt | 2 ++ include/net/ip.h | 1 + net/can/raw.c | 3 +++ net/ipv4/icmp.c | 2 ++ net/ipv4/ip_output.c | 6 ++++++ net/ipv4/raw.c | 1 + net/ipv4/udp.c | 4 ++++ 7 files changed, 19 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt index a681a65b5bc7..0e58b4539176 100644 --- a/Documentation/networking/timestamping.txt +++ b/Documentation/networking/timestamping.txt @@ -56,6 +56,8 @@ and including the link layer, the scm_timestamping control message and a sock_extended_err control message with ee_errno==ENOMSG and ee_origin==SO_EE_ORIGIN_TIMESTAMPING. A socket with such a pending bounced packet is ready for reading as far as select() is concerned. +If the outgoing packet has to be fragmented, then only the first +fragment is time stamped and returned to the sending socket. All three values correspond to the same event in time, but were generated in different ways. Each of these values may be empty (= all diff --git a/include/net/ip.h b/include/net/ip.h index 10868139e656..4ac7577f98d0 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -55,6 +55,7 @@ struct ipcm_cookie __be32 addr; int oif; struct ip_options *opt; + union skb_shared_tx shtx; }; #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb)) diff --git a/net/can/raw.c b/net/can/raw.c index 0703cba4bf9f..6aa154e806ae 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -646,6 +646,9 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, goto put_dev; err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + if (err < 0) + goto free_skb; + err = sock_tx_timestamp(msg, sk, skb_tx(skb)); if (err < 0) goto free_skb; skb->dev = dev; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 705b33b184a3..382800a62b31 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -375,6 +375,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) inet->tos = ip_hdr(skb)->tos; daddr = ipc.addr = rt->rt_src; ipc.opt = NULL; + ipc.shtx.flags = 0; if (icmp_param->replyopts.optlen) { ipc.opt = &icmp_param->replyopts; if (ipc.opt->srr) @@ -532,6 +533,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) inet_sk(sk)->tos = tos; ipc.addr = iph->saddr; ipc.opt = &icmp_param.replyopts; + ipc.shtx.flags = 0; { struct flowi fl = { diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 8ebe86dd72af..3e7e910c7c0f 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -935,6 +935,10 @@ alloc_new_skb: sk->sk_allocation); if (unlikely(skb == NULL)) err = -ENOBUFS; + else + /* only the initial fragment is + time stamped */ + ipc->shtx.flags = 0; } if (skb == NULL) goto error; @@ -945,6 +949,7 @@ alloc_new_skb: skb->ip_summed = csummode; skb->csum = 0; skb_reserve(skb, hh_len); + *skb_tx(skb) = ipc->shtx; /* * Find where to start putting bytes. @@ -1364,6 +1369,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar daddr = ipc.addr = rt->rt_src; ipc.opt = NULL; + ipc.shtx.flags = 0; if (replyopts.opt.optlen) { ipc.opt = &replyopts.opt; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index dff8bc4e0fac..f774651f0a47 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -493,6 +493,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.addr = inet->saddr; ipc.opt = NULL; + ipc.shtx.flags = 0; ipc.oif = sk->sk_bound_dev_if; if (msg->msg_controllen) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c47c989cb1fb..4bd178a111d5 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -596,6 +596,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, return -EOPNOTSUPP; ipc.opt = NULL; + ipc.shtx.flags = 0; if (up->pending) { /* @@ -643,6 +644,9 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.addr = inet->saddr; ipc.oif = sk->sk_bound_dev_if; + err = sock_tx_timestamp(msg, sk, &ipc.shtx); + if (err) + return err; if (msg->msg_controllen) { err = ip_cmsg_send(sock_net(sk), msg, &ipc); if (err) -- cgit v1.2.3-59-g8ed1b From c844a5d38e4247fc71e371221cf762a2a44d565b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Feb 2009 23:17:33 +0100 Subject: ALSA: Fix documentation for snd-cs4236 driver Updated; removal of snd-cs4232 entry. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/ALSA-Configuration.txt | 30 +++++-------------------- 1 file changed, 5 insertions(+), 25 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index a763b76afe51..57fe4f3ca2c0 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -391,34 +391,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. The power-management is supported. - Module snd-cs4232 - ----------------- - - Module for sound cards based on CS4232/CS4232A ISA chips. - - isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) - - with isapnp=0, the following options are available: - - port - port # for CS4232 chip (PnP setup - 0x534) - cport - control port # for CS4232 chip (PnP setup - 0x120,0x210,0xf00) - mpu_port - port # for MPU-401 UART (PnP setup - 0x300), -1 = disable - fm_port - FM port # for CS4232 chip (PnP setup - 0x388), -1 = disable - irq - IRQ # for CS4232 chip (5,7,9,11,12,15) - mpu_irq - IRQ # for MPU-401 UART (9,11,12,15) - dma1 - first DMA # for CS4232 chip (0,1,3) - dma2 - second DMA # for Yamaha CS4232 chip (0,1,3), -1 = disable - - This module supports multiple cards. This module does not support autoprobe - (if ISA PnP is not used) thus main port must be specified!!! Other ports are - optional. - - The power-management is supported. - Module snd-cs4236 ----------------- - Module for sound cards based on CS4235/CS4236/CS4236B/CS4237B/ + Module for sound cards based on CS4232/CS4232A, + CS4235/CS4236/CS4236B/CS4237B/ CS4238B/CS4239 ISA chips. isapnp - ISA PnP detection - 0 = disable, 1 = enable (default) @@ -440,6 +417,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. The power-management is supported. + This module is aliased as snd-cs4232 since it provides the old + snd-cs4232 functionality, too. + Module snd-cs4281 ----------------- -- cgit v1.2.3-59-g8ed1b From 4dd3a29f295799295eac819bbf540690fbe30c16 Mon Sep 17 00:00:00 2001 From: Yoichi Yuasa Date: Wed, 18 Feb 2009 19:09:23 +0900 Subject: sound: fix opensound URL in oss Introduction Signed-off-by: Yoichi Yuasa Signed-off-by: Takashi Iwai --- Documentation/sound/oss/Introduction | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/sound/oss/Introduction b/Documentation/sound/oss/Introduction index f04ba6bb7395..75d967ff9266 100644 --- a/Documentation/sound/oss/Introduction +++ b/Documentation/sound/oss/Introduction @@ -80,7 +80,7 @@ Notes: additional features. 2. The commercial OSS driver may be obtained from the site: - http://www/opensound.com. This may be used for cards that + http://www.opensound.com. This may be used for cards that are unsupported by the kernel driver, or may be used by other operating systems. -- cgit v1.2.3-59-g8ed1b From eca985d28e1a8092ba2686ec5485fd688df5cfb3 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Wed, 18 Feb 2009 19:07:18 +0100 Subject: sound: Remove documentation for OSS CS4232 driver There is no OSS cs4232 driver in the kernel any more and this documentation does not contain any info useful for ALSA driver. Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai --- Documentation/sound/oss/CS4232 | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 Documentation/sound/oss/CS4232 (limited to 'Documentation') diff --git a/Documentation/sound/oss/CS4232 b/Documentation/sound/oss/CS4232 deleted file mode 100644 index 7d6af7a5c1c2..000000000000 --- a/Documentation/sound/oss/CS4232 +++ /dev/null @@ -1,23 +0,0 @@ -To configure the Crystal CS423x sound chip and activate its DSP functions, -modules may be loaded in this order: - - modprobe sound - insmod ad1848 - insmod uart401 - insmod cs4232 io=* irq=* dma=* dma2=* - -This is the meaning of the parameters: - - io--I/O address of the Windows Sound System (normally 0x534) - irq--IRQ of this device - dma and dma2--DMA channels (DMA2 may be 0) - -On some cards, the board attempts to do non-PnP setup, and fails. If you -have problems, use Linux' PnP facilities. - -To get MIDI facilities add - - insmod opl3 io=* - -where "io" is the I/O address of the OPL3 synthesizer. This will be shown -in /proc/sys/pnp and is normally 0x388. -- cgit v1.2.3-59-g8ed1b From f1085c4f319f1e43c95718045a235f276cc4b615 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 20 Feb 2009 14:50:35 +0100 Subject: ALSA: hda - Update documentation for pincfg sysfs entries Added the brief descriptions of new sysfs entries for pint default config values. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio.txt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index 99f7fbbe3e67..9c51e104546f 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -365,10 +365,22 @@ modelname:: to this file. init_verbs:: The extra verbs to execute at initialization. You can add a verb by - writing to this file. Pass tree numbers, nid, verb and parameter. + writing to this file. Pass three numbers: nid, verb and parameter. hints:: Shows hint strings for codec parsers for any use. Right now it's not used. +init_pin_configs:: + Shows the initial pin default config values set by BIOS. +override_pin_configs:: + Shows the pin default config values to override the BIOS setup. + Writing this (with two numbers, NID and value) appends the new + value. The given will be used instead of the initial BIOS value at + the next reconfiguration time. +cur_pin_configs:: + Shows the pin default values set by the codec parser explicitly. + This doesn't show all pin values but only the changed values by + the parser. That is, if the parser doesn't change the pin default + config values by itself, this will contain nothing. reconfig:: Triggers the codec re-configuration. When any value is written to this file, the driver re-initialize and parses the codec tree -- cgit v1.2.3-59-g8ed1b From b98103a5597b87211a1c74077b06faeac554bedc Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Sat, 21 Feb 2009 00:09:47 +0100 Subject: x86: hpet: print HPET registers during setup (if hpet=verbose is used) Signed-off-by: Andreas Herrmann Cc: Mark Hounschell Cc: Borislav Petkov Signed-off-by: Ingo Molnar --- Documentation/kernel-parameters.txt | 4 +++- arch/x86/kernel/hpet.c | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index b182626739ea..01379a822646 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -492,10 +492,12 @@ and is between 256 and 4096 characters. It is defined in the file Default: 64 hpet= [X86-32,HPET] option to control HPET usage - Format: { enable (default) | disable | force } + Format: { enable (default) | disable | force | + verbose } disable: disable HPET and use PIT instead force: allow force enabled of undocumented chips (ICH4, VIA, nVidia) + verbose: show contents of HPET registers during setup com20020= [HW,NET] ARCnet - COM20020 chipset Format: diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index a00545fe5cdd..1d86ca3c1f97 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -80,6 +80,7 @@ static inline void hpet_clear_mapping(void) */ static int boot_hpet_disable; int hpet_force_user; +static int hpet_verbose; static int __init hpet_setup(char *str) { @@ -88,6 +89,8 @@ static int __init hpet_setup(char *str) boot_hpet_disable = 1; if (!strncmp("force", str, 5)) hpet_force_user = 1; + if (!strncmp("verbose", str, 7)) + hpet_verbose = 1; } return 1; } @@ -119,6 +122,43 @@ int is_hpet_enabled(void) } EXPORT_SYMBOL_GPL(is_hpet_enabled); +static void _hpet_print_config(const char *function, int line) +{ + u32 i, timers, l, h; + printk(KERN_INFO "hpet: %s(%d):\n", function, line); + l = hpet_readl(HPET_ID); + h = hpet_readl(HPET_PERIOD); + timers = ((l & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; + printk(KERN_INFO "hpet: ID: 0x%x, PERIOD: 0x%x\n", l, h); + l = hpet_readl(HPET_CFG); + h = hpet_readl(HPET_STATUS); + printk(KERN_INFO "hpet: CFG: 0x%x, STATUS: 0x%x\n", l, h); + l = hpet_readl(HPET_COUNTER); + h = hpet_readl(HPET_COUNTER+4); + printk(KERN_INFO "hpet: COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); + + for (i = 0; i < timers; i++) { + l = hpet_readl(HPET_Tn_CFG(i)); + h = hpet_readl(HPET_Tn_CFG(i)+4); + printk(KERN_INFO "hpet: T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", + i, l, h); + l = hpet_readl(HPET_Tn_CMP(i)); + h = hpet_readl(HPET_Tn_CMP(i)+4); + printk(KERN_INFO "hpet: T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", + i, l, h); + l = hpet_readl(HPET_Tn_ROUTE(i)); + h = hpet_readl(HPET_Tn_ROUTE(i)+4); + printk(KERN_INFO "hpet: T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", + i, l, h); + } +} + +#define hpet_print_config() \ +do { \ + if (hpet_verbose) \ + _hpet_print_config(__FUNCTION__, __LINE__); \ +} while (0) + /* * When the hpet driver (/dev/hpet) is enabled, we need to reserve * timer 0 and timer 1 in case of RTC emulation. @@ -282,6 +322,7 @@ static void hpet_set_mode(enum clock_event_mode mode, hpet_writel(cmp, HPET_Tn_CMP(timer)); udelay(1); hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); + hpet_print_config(); break; case CLOCK_EVT_MODE_ONESHOT: @@ -308,6 +349,7 @@ static void hpet_set_mode(enum clock_event_mode mode, irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); enable_irq(hdev->irq); } + hpet_print_config(); break; } } @@ -526,6 +568,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); num_timers++; /* Value read out starts from 0 */ + hpet_print_config(); hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL); if (!hpet_devs) @@ -793,6 +836,7 @@ int __init hpet_enable(void) * information and the number of channels */ id = hpet_readl(HPET_ID); + hpet_print_config(); #ifdef CONFIG_HPET_EMULATE_RTC /* @@ -845,6 +889,7 @@ static __init int hpet_late_init(void) return -ENODEV; hpet_reserve_platform_timers(hpet_readl(HPET_ID)); + hpet_print_config(); for_each_online_cpu(cpu) { hpet_cpuhp_notify(NULL, CPU_ONLINE, (void *)(long)cpu); -- cgit v1.2.3-59-g8ed1b From c1cf8422f0512c2b14f0d66bce34abb0645c888a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 20 Feb 2009 08:25:36 +0000 Subject: ip: add loose reverse path filtering Extend existing reverse path filter option to allow strict or loose filtering. (See http://en.wikipedia.org/wiki/Reverse_path_filtering). For compatibility with existing usage, the value 1 is chosen for strict mode and 2 for loose mode. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 24 +++++++++++++++--------- net/ipv4/fib_frontend.c | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index ff3f219ee4d7..71041c21af9b 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -699,16 +699,22 @@ accept_source_route - BOOLEAN default TRUE (router) FALSE (host) -rp_filter - BOOLEAN - 1 - do source validation by reversed path, as specified in RFC1812 - Recommended option for single homed hosts and stub network - routers. Could cause troubles for complicated (not loop free) - networks running a slow unreliable protocol (sort of RIP), - or using static routes. - +rp_filter - INTEGER 0 - No source validation. - - conf/all/rp_filter must also be set to TRUE to do source validation + 1 - Strict mode as defined in RFC3704 Strict Reverse Path + Each incoming packet is tested against the FIB and if the interface + is not the best reverse path the packet check will fail. + By default failed packets are discarded. + 2 - Loose mode as defined in RFC3704 Loose Reverse Path + Each incoming packet's source address is also tested against the FIB + and if the source address is not reachable via any interface + the packet check will fail. + + Current recommended practice in RFC3704 is to enable strict mode + to prevent IP spoofin from DDos attacks. If using asymmetric routing + or other complicated routing,t hen loose mode is recommended. + + conf/all/rp_filter must also be set to non-zero to do source validation on the interface Default value is 0. Note that some distributions enable it diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 741e4fa3e474..cafcc49d0993 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -275,7 +275,7 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif, fib_res_put(&res); if (no_addr) goto last_resort; - if (rpf) + if (rpf == 1) goto e_inval; fl.oif = dev->ifindex; -- cgit v1.2.3-59-g8ed1b From 722bde6875bfb49a0c84e5601eb82dd7ac02d27c Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 23 Feb 2009 00:51:57 -0500 Subject: ext4: Add fine print for the 32000 subdirectory limit Some poeple are reading the ext4 feature list too literally and create dubious test cases involving very long filenames and 1k blocksize and then complain when they run into an htree-imposed limit. So add fine print to the "fix 32000 subdirectory limit" ext4 feature. Signed-off-by: "Theodore Ts'o" --- Documentation/filesystems/ext4.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index cec829bc7291..5c484aec2bab 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -85,7 +85,7 @@ Note: More extensive information for getting started with ext4 can be * extent format more robust in face of on-disk corruption due to magics, * internal redundancy in tree * improved file allocation (multi-block alloc) -* fix 32000 subdirectory limit +* lift 32000 subdirectory limit imposed by i_links_count[1] * nsec timestamps for mtime, atime, ctime, create time * inode version field on disk (NFSv4, Lustre) * reduced e2fsck time via uninit_bg feature @@ -100,6 +100,9 @@ Note: More extensive information for getting started with ext4 can be * efficent new ordered mode in JBD2 and ext4(avoid using buffer head to force the ordering) +[1] Filesystems with a block size of 1k may see a limit imposed by the +directory hash tree having a maximum depth of two. + 2.2 Candidate features for future inclusion * Online defrag (patches available but not well tested) -- cgit v1.2.3-59-g8ed1b From 346ff70fdbe9093947b9494fe714c89cafcceade Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Feb 2009 09:42:57 +0100 Subject: ALSA: hda - Rename {override,cur}_pin with {user,driver}_pin Rename from override_pin and cur_pin with user_pin and driver_pin, respectively, to be a bit more intuitive. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio.txt | 12 ++++++------ sound/pci/hda/hda_codec.c | 18 +++++++++--------- sound/pci/hda/hda_codec.h | 4 ++-- sound/pci/hda/hda_hwdep.c | 32 ++++++++++++++++---------------- 4 files changed, 33 insertions(+), 33 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index 9c51e104546f..f590850c149f 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -371,16 +371,16 @@ hints:: not used. init_pin_configs:: Shows the initial pin default config values set by BIOS. -override_pin_configs:: - Shows the pin default config values to override the BIOS setup. - Writing this (with two numbers, NID and value) appends the new - value. The given will be used instead of the initial BIOS value at - the next reconfiguration time. -cur_pin_configs:: +driver_pin_configs:: Shows the pin default values set by the codec parser explicitly. This doesn't show all pin values but only the changed values by the parser. That is, if the parser doesn't change the pin default config values by itself, this will contain nothing. +user_pin_configs:: + Shows the pin default config values to override the BIOS setup. + Writing this (with two numbers, NID and value) appends the new + value. The given will be used instead of the initial BIOS value at + the next reconfiguration time. reconfig:: Triggers the codec re-configuration. When any value is written to this file, the driver re-initialize and parses the codec tree diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8ec2dfca9a6c..df9453d0122e 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -755,7 +755,7 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, unsigned int cfg) { - return snd_hda_add_pincfg(codec, &codec->cur_pins, nid, cfg); + return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg); } EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg); @@ -764,11 +764,11 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) { struct hda_pincfg *pin; - pin = look_up_pincfg(codec, &codec->cur_pins, nid); + pin = look_up_pincfg(codec, &codec->driver_pins, nid); if (pin) return pin->cfg; #ifdef CONFIG_SND_HDA_HWDEP - pin = look_up_pincfg(codec, &codec->override_pins, nid); + pin = look_up_pincfg(codec, &codec->user_pins, nid); if (pin) return pin->cfg; #endif @@ -797,12 +797,12 @@ static void free_hda_cache(struct hda_cache_rec *cache); /* restore the initial pin cfgs and release all pincfg lists */ static void restore_init_pincfgs(struct hda_codec *codec) { - /* first free cur_pins and override_pins, then call restore_pincfg + /* first free driver_pins and user_pins, then call restore_pincfg * so that only the values in init_pins are restored */ - snd_array_free(&codec->cur_pins); + snd_array_free(&codec->driver_pins); #ifdef CONFIG_SND_HDA_HWDEP - snd_array_free(&codec->override_pins); + snd_array_free(&codec->user_pins); #endif restore_pincfgs(codec); snd_array_free(&codec->init_pins); @@ -874,7 +874,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); - snd_array_init(&codec->cur_pins, sizeof(struct hda_pincfg), 16); + snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); if (codec->bus->modelname) { codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); if (!codec->modelname) { @@ -1463,8 +1463,8 @@ void snd_hda_codec_reset(struct hda_codec *codec) free_hda_cache(&codec->cmd_cache); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); - /* free only cur_pins so that init_pins + override_pins are restored */ - snd_array_free(&codec->cur_pins); + /* free only driver_pins so that init_pins + user_pins are restored */ + snd_array_free(&codec->driver_pins); restore_pincfgs(codec); codec->num_pcms = 0; codec->pcm_info = NULL; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 6d01a8058f0a..2ea628478a91 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -779,13 +779,13 @@ struct hda_codec { unsigned int spdif_in_enable; /* SPDIF input enable? */ hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ struct snd_array init_pins; /* initial (BIOS) pin configurations */ - struct snd_array cur_pins; /* current pin configurations */ + struct snd_array driver_pins; /* pin configs set by codec parser */ #ifdef CONFIG_SND_HDA_HWDEP struct snd_hwdep *hwdep; /* assigned hwdep device */ struct snd_array init_verbs; /* additional init verbs */ struct snd_array hints; /* additional hints */ - struct snd_array override_pins; /* default pin configs to override */ + struct snd_array user_pins; /* default pin configs to override */ #endif /* misc flags */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 71039a6dec28..c660383ef381 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -109,7 +109,7 @@ static void clear_hwdep_elements(struct hda_codec *codec) for (i = 0; i < codec->hints.used; i++, head++) kfree(*head); snd_array_free(&codec->hints); - snd_array_free(&codec->override_pins); + snd_array_free(&codec->user_pins); } static void hwdep_free(struct snd_hwdep *hwdep) @@ -142,7 +142,7 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); snd_array_init(&codec->hints, sizeof(char *), 32); - snd_array_init(&codec->override_pins, sizeof(struct hda_pincfg), 16); + snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16); return 0; } @@ -340,29 +340,29 @@ static ssize_t init_pin_configs_show(struct device *dev, return pin_configs_show(codec, &codec->init_pins, buf); } -static ssize_t override_pin_configs_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t user_pin_configs_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct hda_codec *codec = hwdep->private_data; - return pin_configs_show(codec, &codec->override_pins, buf); + return pin_configs_show(codec, &codec->user_pins, buf); } -static ssize_t cur_pin_configs_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t driver_pin_configs_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct hda_codec *codec = hwdep->private_data; - return pin_configs_show(codec, &codec->cur_pins, buf); + return pin_configs_show(codec, &codec->driver_pins, buf); } #define MAX_PIN_CONFIGS 32 -static ssize_t override_pin_configs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t user_pin_configs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct hda_codec *codec = hwdep->private_data; @@ -373,7 +373,7 @@ static ssize_t override_pin_configs_store(struct device *dev, return -EINVAL; if (!nid) return -EINVAL; - err = snd_hda_add_pincfg(codec, &codec->override_pins, nid, cfg); + err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); if (err < 0) return err; return count; @@ -397,8 +397,8 @@ static struct device_attribute codec_attrs[] = { CODEC_ATTR_WO(init_verbs), CODEC_ATTR_WO(hints), CODEC_ATTR_RO(init_pin_configs), - CODEC_ATTR_RW(override_pin_configs), - CODEC_ATTR_RO(cur_pin_configs), + CODEC_ATTR_RW(user_pin_configs), + CODEC_ATTR_RO(driver_pin_configs), CODEC_ATTR_WO(reconfig), CODEC_ATTR_WO(clear), }; -- cgit v1.2.3-59-g8ed1b From 5e7b8e0d87091ae21b291588817b5359a5e00795 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Feb 2009 09:45:59 +0100 Subject: ALSA: hda - Make user_pin overriding the driver setup Make user_pin overriding even the driver pincfg, e.g. the static / fixed pin config table in patch_sigmatel.c. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio.txt | 3 ++- sound/pci/hda/hda_codec.c | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index f590850c149f..a4e5ef87af6b 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -380,7 +380,8 @@ user_pin_configs:: Shows the pin default config values to override the BIOS setup. Writing this (with two numbers, NID and value) appends the new value. The given will be used instead of the initial BIOS value at - the next reconfiguration time. + the next reconfiguration time. Note that this config will override + even the driver pin configs, too. reconfig:: Triggers the codec re-configuration. When any value is written to this file, the driver re-initialize and parses the codec tree diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index df9453d0122e..a13480fa8e74 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -739,7 +739,9 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, hda_nid_t nid, unsigned int cfg) { struct hda_pincfg *pin; + unsigned int oldcfg; + oldcfg = snd_hda_codec_get_pincfg(codec, nid); pin = look_up_pincfg(codec, list, nid); if (!pin) { pin = snd_array_new(list); @@ -748,7 +750,13 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, pin->nid = nid; } pin->cfg = cfg; - set_pincfg(codec, nid, cfg); + + /* change only when needed; e.g. if the pincfg is already present + * in user_pins[], don't write it + */ + cfg = snd_hda_codec_get_pincfg(codec, nid); + if (oldcfg != cfg) + set_pincfg(codec, nid, cfg); return 0; } @@ -764,14 +772,14 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) { struct hda_pincfg *pin; - pin = look_up_pincfg(codec, &codec->driver_pins, nid); - if (pin) - return pin->cfg; #ifdef CONFIG_SND_HDA_HWDEP pin = look_up_pincfg(codec, &codec->user_pins, nid); if (pin) return pin->cfg; #endif + pin = look_up_pincfg(codec, &codec->driver_pins, nid); + if (pin) + return pin->cfg; pin = look_up_pincfg(codec, &codec->init_pins, nid); if (pin) return pin->cfg; -- cgit v1.2.3-59-g8ed1b From 39c2871eeaeeddcbecee29ec905ec528a057ca52 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Feb 2009 14:14:51 +0100 Subject: ALSA: hda - Add an example about pin reconfiguration Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index a4e5ef87af6b..99958be7b454 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -391,6 +391,14 @@ clear:: Resets the codec, removes the mixer elements and PCM stuff of the specified codec, and clear all init verbs and hints. +For example, when you want to change the pin default configuration +value of the pin widget 0x14 to 0x9993013f, and let the driver +re-configure based on that state, run like below: +------------------------------------------------------------------------ + # echo 0x14 0x9993013f > /sys/class/sound/hwC0D0/user_pin_configs + # echo 1 > /sys/class/sound/hwC0D0/reconfig +------------------------------------------------------------------------ + Power-Saving ~~~~~~~~~~~~ -- cgit v1.2.3-59-g8ed1b From bf869c30628cc02295fb919f0d0074f296d5f129 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 23 Feb 2009 04:37:55 +0000 Subject: Doc: Fix typos in ip-sysctl.txt about rp_filter. First fix a typo in Stephens patch ;-) Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 71041c21af9b..e0b8c2c61710 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -711,8 +711,8 @@ rp_filter - INTEGER the packet check will fail. Current recommended practice in RFC3704 is to enable strict mode - to prevent IP spoofin from DDos attacks. If using asymmetric routing - or other complicated routing,t hen loose mode is recommended. + to prevent IP spoofing from DDos attacks. If using asymmetric routing + or other complicated routing, then loose mode is recommended. conf/all/rp_filter must also be set to non-zero to do source validation on the interface -- cgit v1.2.3-59-g8ed1b From e18f5feb0c851a0e77e315b3d9ef1c432b1a50ec Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 23 Feb 2009 04:39:04 +0000 Subject: Doc: Cleanup whitespaces in ip-sysctl.txt Fix up whitespaces while going though ip-sysctl.txt anyway. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 118 ++++++++++++++++----------------- 1 file changed, 59 insertions(+), 59 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index e0b8c2c61710..7185e4c41e59 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -2,7 +2,7 @@ ip_forward - BOOLEAN 0 - disabled (default) - not 0 - enabled + not 0 - enabled Forward Packets between interfaces. @@ -36,49 +36,49 @@ rt_cache_rebuild_count - INTEGER IP Fragmentation: ipfrag_high_thresh - INTEGER - Maximum memory used to reassemble IP fragments. When + Maximum memory used to reassemble IP fragments. When ipfrag_high_thresh bytes of memory is allocated for this purpose, the fragment handler will toss packets until ipfrag_low_thresh is reached. - + ipfrag_low_thresh - INTEGER - See ipfrag_high_thresh + See ipfrag_high_thresh ipfrag_time - INTEGER - Time in seconds to keep an IP fragment in memory. + Time in seconds to keep an IP fragment in memory. ipfrag_secret_interval - INTEGER - Regeneration interval (in seconds) of the hash secret (or lifetime + Regeneration interval (in seconds) of the hash secret (or lifetime for the hash secret) for IP fragments. Default: 600 ipfrag_max_dist - INTEGER - ipfrag_max_dist is a non-negative integer value which defines the - maximum "disorder" which is allowed among fragments which share a - common IP source address. Note that reordering of packets is - not unusual, but if a large number of fragments arrive from a source - IP address while a particular fragment queue remains incomplete, it - probably indicates that one or more fragments belonging to that queue - have been lost. When ipfrag_max_dist is positive, an additional check - is done on fragments before they are added to a reassembly queue - if - ipfrag_max_dist (or more) fragments have arrived from a particular IP - address between additions to any IP fragment queue using that source - address, it's presumed that one or more fragments in the queue are - lost. The existing fragment queue will be dropped, and a new one + ipfrag_max_dist is a non-negative integer value which defines the + maximum "disorder" which is allowed among fragments which share a + common IP source address. Note that reordering of packets is + not unusual, but if a large number of fragments arrive from a source + IP address while a particular fragment queue remains incomplete, it + probably indicates that one or more fragments belonging to that queue + have been lost. When ipfrag_max_dist is positive, an additional check + is done on fragments before they are added to a reassembly queue - if + ipfrag_max_dist (or more) fragments have arrived from a particular IP + address between additions to any IP fragment queue using that source + address, it's presumed that one or more fragments in the queue are + lost. The existing fragment queue will be dropped, and a new one started. An ipfrag_max_dist value of zero disables this check. Using a very small value, e.g. 1 or 2, for ipfrag_max_dist can result in unnecessarily dropping fragment queues when normal - reordering of packets occurs, which could lead to poor application - performance. Using a very large value, e.g. 50000, increases the - likelihood of incorrectly reassembling IP fragments that originate + reordering of packets occurs, which could lead to poor application + performance. Using a very large value, e.g. 50000, increases the + likelihood of incorrectly reassembling IP fragments that originate from different IP datagrams, which could result in data corruption. Default: 64 INET peer storage: inet_peer_threshold - INTEGER - The approximate size of the storage. Starting from this threshold + The approximate size of the storage. Starting from this threshold entries will be thrown aggressively. This threshold also determines entries' time-to-live and time intervals between garbage collection passes. More entries, less time-to-live, less GC interval. @@ -105,7 +105,7 @@ inet_peer_gc_maxtime - INTEGER in effect under low (or absent) memory pressure on the pool. Measured in seconds. -TCP variables: +TCP variables: somaxconn - INTEGER Limit of socket listen() backlog, known in userspace as SOMAXCONN. @@ -310,7 +310,7 @@ tcp_orphan_retries - INTEGER tcp_reordering - INTEGER Maximal reordering of packets in a TCP stream. - Default: 3 + Default: 3 tcp_retrans_collapse - BOOLEAN Bug-to-bug compatibility with some broken printers. @@ -521,7 +521,7 @@ IP Variables: ip_local_port_range - 2 INTEGERS Defines the local port range that is used by TCP and UDP to - choose the local port. The first number is the first, the + choose the local port. The first number is the first, the second the last local port number. Default value depends on amount of memory available on the system: > 128Mb 32768-61000 @@ -594,12 +594,12 @@ icmp_errors_use_inbound_ifaddr - BOOLEAN If zero, icmp error messages are sent with the primary address of the exiting interface. - + If non-zero, the message will be sent with the primary address of the interface that received the packet that caused the icmp error. This is the behaviour network many administrators will expect from a router. And it can make debugging complicated network layouts - much easier. + much easier. Note that if no primary address exists for the interface selected, then the primary address of the first non-loopback interface that @@ -611,7 +611,7 @@ igmp_max_memberships - INTEGER Change the maximum number of multicast groups we can subscribe to. Default: 20 -conf/interface/* changes special settings per interface (where "interface" is +conf/interface/* changes special settings per interface (where "interface" is the name of your network interface) conf/all/* is special, changes the settings for all interfaces @@ -625,11 +625,11 @@ log_martians - BOOLEAN accept_redirects - BOOLEAN Accept ICMP redirect messages. accept_redirects for the interface will be enabled if: - - both conf/{all,interface}/accept_redirects are TRUE in the case forwarding - for the interface is enabled + - both conf/{all,interface}/accept_redirects are TRUE in the case + forwarding for the interface is enabled or - - at least one of conf/{all,interface}/accept_redirects is TRUE in the case - forwarding for the interface is disabled + - at least one of conf/{all,interface}/accept_redirects is TRUE in the + case forwarding for the interface is disabled accept_redirects for the interface will be disabled otherwise default TRUE (host) FALSE (router) @@ -640,8 +640,8 @@ forwarding - BOOLEAN mc_forwarding - BOOLEAN Do multicast routing. The kernel needs to be compiled with CONFIG_MROUTE and a multicast routing daemon is required. - conf/all/mc_forwarding must also be set to TRUE to enable multicast routing - for the interface + conf/all/mc_forwarding must also be set to TRUE to enable multicast + routing for the interface medium_id - INTEGER Integer value used to differentiate the devices by the medium they @@ -649,7 +649,7 @@ medium_id - INTEGER the broadcast packets are received only on one of them. The default value 0 means that the device is the only interface to its medium, value of -1 means that medium is not known. - + Currently, it is used to change the proxy_arp behavior: the proxy_arp feature is enabled for packets forwarded between two devices attached to different media. @@ -710,9 +710,9 @@ rp_filter - INTEGER and if the source address is not reachable via any interface the packet check will fail. - Current recommended practice in RFC3704 is to enable strict mode + Current recommended practice in RFC3704 is to enable strict mode to prevent IP spoofing from DDos attacks. If using asymmetric routing - or other complicated routing, then loose mode is recommended. + or other complicated routing, then loose mode is recommended. conf/all/rp_filter must also be set to non-zero to do source validation on the interface @@ -835,7 +835,7 @@ apply to IPv6 [XXX?]. bindv6only - BOOLEAN Default value for IPV6_V6ONLY socket option, - which restricts use of the IPv6 socket to IPv6 communication + which restricts use of the IPv6 socket to IPv6 communication only. TRUE: disable IPv4-mapped address feature FALSE: enable IPv4-mapped address feature @@ -845,19 +845,19 @@ bindv6only - BOOLEAN IPv6 Fragmentation: ip6frag_high_thresh - INTEGER - Maximum memory used to reassemble IPv6 fragments. When + Maximum memory used to reassemble IPv6 fragments. When ip6frag_high_thresh bytes of memory is allocated for this purpose, the fragment handler will toss packets until ip6frag_low_thresh is reached. - + ip6frag_low_thresh - INTEGER - See ip6frag_high_thresh + See ip6frag_high_thresh ip6frag_time - INTEGER Time in seconds to keep an IPv6 fragment in memory. ip6frag_secret_interval - INTEGER - Regeneration interval (in seconds) of the hash secret (or lifetime + Regeneration interval (in seconds) of the hash secret (or lifetime for the hash secret) for IPv6 fragments. Default: 600 @@ -866,17 +866,17 @@ conf/default/*: conf/all/*: - Change all the interface-specific settings. + Change all the interface-specific settings. [XXX: Other special features than forwarding?] conf/all/forwarding - BOOLEAN - Enable global IPv6 forwarding between all interfaces. + Enable global IPv6 forwarding between all interfaces. - IPv4 and IPv6 work differently here; e.g. netfilter must be used + IPv4 and IPv6 work differently here; e.g. netfilter must be used to control which interfaces may forward packets and which not. - This also sets all interfaces' Host/Router setting + This also sets all interfaces' Host/Router setting 'forwarding' to the specified value. See below for details. This referred to as global forwarding. @@ -887,12 +887,12 @@ proxy_ndp - BOOLEAN conf/interface/*: Change special settings per interface. - The functional behaviour for certain settings is different + The functional behaviour for certain settings is different depending on whether local forwarding is enabled or not. accept_ra - BOOLEAN Accept Router Advertisements; autoconfigure using them. - + Functional default: enabled if local forwarding is disabled. disabled if local forwarding is enabled. @@ -938,7 +938,7 @@ accept_source_route - INTEGER Default: 0 autoconf - BOOLEAN - Autoconfigure addresses using Prefix Information in Router + Autoconfigure addresses using Prefix Information in Router Advertisements. Functional default: enabled if accept_ra_pinfo is enabled. @@ -947,11 +947,11 @@ autoconf - BOOLEAN dad_transmits - INTEGER The amount of Duplicate Address Detection probes to send. Default: 1 - + forwarding - BOOLEAN - Configure interface-specific Host/Router behaviour. + Configure interface-specific Host/Router behaviour. - Note: It is recommended to have the same setting on all + Note: It is recommended to have the same setting on all interfaces; mixed router/host scenarios are rather uncommon. FALSE: @@ -960,13 +960,13 @@ forwarding - BOOLEAN 1. IsRouter flag is not set in Neighbour Advertisements. 2. Router Solicitations are being sent when necessary. - 3. If accept_ra is TRUE (default), accept Router + 3. If accept_ra is TRUE (default), accept Router Advertisements (and do autoconfiguration). 4. If accept_redirects is TRUE (default), accept Redirects. TRUE: - If local forwarding is enabled, Router behaviour is assumed. + If local forwarding is enabled, Router behaviour is assumed. This means exactly the reverse from the above: 1. IsRouter flag is set in Neighbour Advertisements. @@ -1001,7 +1001,7 @@ router_solicitation_interval - INTEGER Default: 4 router_solicitations - INTEGER - Number of Router Solicitations to send until assuming no + Number of Router Solicitations to send until assuming no routers are present. Default: 3 @@ -1025,11 +1025,11 @@ temp_prefered_lft - INTEGER max_desync_factor - INTEGER Maximum value for DESYNC_FACTOR, which is a random value - that ensures that clients don't synchronize with each + that ensures that clients don't synchronize with each other and generate new addresses at exactly the same time. value is in seconds. Default: 600 - + regen_max_retry - INTEGER Number of attempts before give up attempting to generate valid temporary addresses. @@ -1037,8 +1037,8 @@ regen_max_retry - INTEGER max_addresses - INTEGER Number of maximum addresses per interface. 0 disables limitation. - It is recommended not set too large value (or 0) because it would - be too easy way to crash kernel to allow to create too much of + It is recommended not set too large value (or 0) because it would + be too easy way to crash kernel to allow to create too much of autoconfigured addresses. Default: 16 -- cgit v1.2.3-59-g8ed1b From ed12978453a3845c947695e7ad32bb3ede444813 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Wed, 4 Feb 2009 01:17:41 +0100 Subject: [CPUFREQ] Introduce /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_transition_latency It's not only useful for the ondemand and conservative governors, but also for userspace daemons to know about the HW transition latency of the CPU. It is especially useful for userspace to know about this value when the ondemand or conservative governors are run. The sampling rate control value depends on it and for userspace being able to set sane tuning values there it has to know about the transition latency. Signed-off-by: Thomas Renninger Signed-off-by: Dave Jones --- Documentation/cpu-freq/user-guide.txt | 12 ++++++++++++ drivers/cpufreq/cpufreq.c | 3 +++ 2 files changed, 15 insertions(+) (limited to 'Documentation') diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt index 917918f84fc7..75f41193f3e1 100644 --- a/Documentation/cpu-freq/user-guide.txt +++ b/Documentation/cpu-freq/user-guide.txt @@ -152,6 +152,18 @@ cpuinfo_min_freq : this file shows the minimum operating frequency the processor can run at(in kHz) cpuinfo_max_freq : this file shows the maximum operating frequency the processor can run at(in kHz) +cpuinfo_transition_latency The time it takes on this CPU to + switch between two frequencies in nano + seconds. If unknown or known to be + that high that the driver does not + work with the ondemand governor, -1 + (CPUFREQ_ETERNAL) will be returned. + Using this information can be useful + to choose an appropriate polling + frequency for a kernel governor or + userspace daemon. Make sure to not + switch the frequency too often + resulting in performance loss. scaling_driver : this file shows what cpufreq driver is used to set the frequency on this CPU diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 1867dac35aff..6fe466efb0b5 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -452,6 +452,7 @@ static ssize_t show_##file_name \ show_one(cpuinfo_min_freq, cpuinfo.min_freq); show_one(cpuinfo_max_freq, cpuinfo.max_freq); +show_one(cpuinfo_transition_latency, cpuinfo.transition_latency); show_one(scaling_min_freq, min); show_one(scaling_max_freq, max); show_one(scaling_cur_freq, cur); @@ -659,6 +660,7 @@ __ATTR(_name, 0644, show_##_name, store_##_name) define_one_ro0400(cpuinfo_cur_freq); define_one_ro(cpuinfo_min_freq); define_one_ro(cpuinfo_max_freq); +define_one_ro(cpuinfo_transition_latency); define_one_ro(scaling_available_governors); define_one_ro(scaling_driver); define_one_ro(scaling_cur_freq); @@ -672,6 +674,7 @@ define_one_rw(scaling_setspeed); static struct attribute *default_attrs[] = { &cpuinfo_min_freq.attr, &cpuinfo_max_freq.attr, + &cpuinfo_transition_latency.attr, &scaling_min_freq.attr, &scaling_max_freq.attr, &affected_cpus.attr, -- cgit v1.2.3-59-g8ed1b From 9411b4ef7fcb534fe1582fe02738254e398dd931 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Wed, 4 Feb 2009 11:54:04 +0100 Subject: [CPUFREQ] ondemand/conservative: deprecate sampling_rate{min,max} The same info can be obtained via the transition_latency sysfs file Signed-off-by: Thomas Renninger Signed-off-by: Dave Jones --- Documentation/cpu-freq/governors.txt | 10 ++++++++-- drivers/cpufreq/cpufreq_conservative.c | 15 +++++++++++++++ drivers/cpufreq/cpufreq_ondemand.c | 17 +++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 5b0cfa67aff9..9b1851297d4e 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -119,8 +119,14 @@ want the kernel to look at the CPU usage and to make decisions on what to do about the frequency. Typically this is set to values of around '10000' or more. -show_sampling_rate_(min|max): the minimum and maximum sampling rates -available that you may set 'sampling_rate' to. +show_sampling_rate_(min|max): THIS INTERFACE IS DEPRECATED, DON'T USE IT. +You can use wider ranges now and the general +cpuinfo_transition_latency variable (cmp. with user-guide.txt) can be +used to obtain exactly the same info: +show_sampling_rate_min = transtition_latency * 500 / 1000 +show_sampling_rate_max = transtition_latency * 500000 / 1000 +(divided by 1000 is to illustrate that sampling rate is in us and +transition latency is exported ns). up_threshold: defines what the average CPU usage between the samplings of 'sampling_rate' needs to be for the kernel to make a decision on diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index c6b3c6a02fc9..0912d7ca8cd7 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -140,11 +140,26 @@ static struct notifier_block dbs_cpufreq_notifier_block = { /************************** sysfs interface ************************/ static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf) { + static int print_once; + + if (!print_once) { + printk(KERN_INFO "CPUFREQ: conservative sampling_rate_max " + "sysfs file is deprecated - used by: %s\n", + current->comm); + print_once = 1; + } return sprintf(buf, "%u\n", MAX_SAMPLING_RATE); } static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf) { + static int print_once; + + if (!print_once) { + printk(KERN_INFO "CPUFREQ: conservative sampling_rate_max " + "sysfs file is deprecated - used by: %s\n", current->comm); + print_once = 1; + } return sprintf(buf, "%u\n", MIN_SAMPLING_RATE); } diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 1fa4420eb33c..32ddeaa42244 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -21,6 +21,7 @@ #include #include #include +#include /* * dbs is used in this file as a shortform for demandbased switching @@ -203,11 +204,27 @@ static void ondemand_powersave_bias_init(void) /************************** sysfs interface ************************/ static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf) { + static int print_once; + + if (!print_once) { + printk(KERN_INFO "CPUFREQ: ondemand sampling_rate_max " + "sysfs file is deprecated - used by: %s\n", + current->comm); + print_once = 1; + } return sprintf(buf, "%u\n", MAX_SAMPLING_RATE); } static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf) { + static int print_once; + + if (!print_once) { + printk(KERN_INFO "CPUFREQ: ondemand sampling_rate_min " + "sysfs file is deprecated - used by: %s\n", + current->comm); + print_once = 1; + } return sprintf(buf, "%u\n", MIN_SAMPLING_RATE); } -- cgit v1.2.3-59-g8ed1b From 112124ab0a9f507a0d7fdbb1e1ed2b9a24f8c4ea Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Wed, 4 Feb 2009 11:55:12 +0100 Subject: [CPUFREQ] ondemand/conservative: sanitize sampling_rate restrictions Limit sampling rate to transition_latency * 100 or kernel limits. If sampling_rate is tried to be set too low, set the lowest allowed value. Signed-off-by: Thomas Renninger Signed-off-by: Dave Jones --- Documentation/cpu-freq/governors.txt | 14 +++++++++++++- drivers/cpufreq/cpufreq_conservative.c | 28 ++++++++++++++++++---------- drivers/cpufreq/cpufreq_ondemand.c | 28 ++++++++++++++++++---------- 3 files changed, 49 insertions(+), 21 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 9b1851297d4e..ce73f3eb5ddb 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -117,7 +117,19 @@ accessible parameters: sampling_rate: measured in uS (10^-6 seconds), this is how often you want the kernel to look at the CPU usage and to make decisions on what to do about the frequency. Typically this is set to values of -around '10000' or more. +around '10000' or more. It's default value is (cmp. with users-guide.txt): +transition_latency * 1000 +The lowest value you can set is: +transition_latency * 100 or it may get restricted to a value where it +makes not sense for the kernel anymore to poll that often which depends +on your HZ config variable (HZ=1000: max=20000us, HZ=250: max=5000). +Be aware that transition latency is in ns and sampling_rate is in us, so you +get the same sysfs value by default. +Sampling rate should always get adjusted considering the transition latency +To set the sampling rate 750 times as high as the transition latency +in the bash (as said, 1000 is default), do: +echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) \ + >ondemand/sampling_rate show_sampling_rate_(min|max): THIS INTERFACE IS DEPRECATED, DON'T USE IT. You can use wider ranges now and the general diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 0912d7ca8cd7..8d541c69aec6 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -54,8 +54,20 @@ static unsigned int def_sampling_rate; (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10)) #define MIN_SAMPLING_RATE \ (def_sampling_rate / MIN_SAMPLING_RATE_RATIO) +/* Above MIN_SAMPLING_RATE will vanish with its sysfs file soon + * Define the minimal settable sampling rate to the greater of: + * - "HW transition latency" * 100 (same as default sampling / 10) + * - MIN_STAT_SAMPLING_RATE + * To avoid that userspace shoots itself. +*/ +static unsigned int minimum_sampling_rate(void) +{ + return max(def_sampling_rate / 10, MIN_STAT_SAMPLING_RATE); +} + +/* This will also vanish soon with removing sampling_rate_max */ #define MAX_SAMPLING_RATE (500 * def_sampling_rate) -#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000) +#define LATENCY_MULTIPLIER (1000) #define DEF_SAMPLING_DOWN_FACTOR (1) #define MAX_SAMPLING_DOWN_FACTOR (10) #define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) @@ -208,13 +220,11 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused, ret = sscanf(buf, "%u", &input); mutex_lock(&dbs_mutex); - if (ret != 1 || input > MAX_SAMPLING_RATE || - input < MIN_SAMPLING_RATE) { + if (ret != 1) { mutex_unlock(&dbs_mutex); return -EINVAL; } - - dbs_tuners_ins.sampling_rate = input; + dbs_tuners_ins.sampling_rate = max(input, minimum_sampling_rate()); mutex_unlock(&dbs_mutex); return count; @@ -540,11 +550,9 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, if (latency == 0) latency = 1; - def_sampling_rate = 10 * latency * - DEF_SAMPLING_RATE_LATENCY_MULTIPLIER; - - if (def_sampling_rate < MIN_STAT_SAMPLING_RATE) - def_sampling_rate = MIN_STAT_SAMPLING_RATE; + def_sampling_rate = + max(10 * latency * LATENCY_MULTIPLIER, + MIN_STAT_SAMPLING_RATE); dbs_tuners_ins.sampling_rate = def_sampling_rate; diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 32ddeaa42244..338f428a15b7 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -52,8 +52,20 @@ static unsigned int def_sampling_rate; (MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10)) #define MIN_SAMPLING_RATE \ (def_sampling_rate / MIN_SAMPLING_RATE_RATIO) +/* Above MIN_SAMPLING_RATE will vanish with its sysfs file soon + * Define the minimal settable sampling rate to the greater of: + * - "HW transition latency" * 100 (same as default sampling / 10) + * - MIN_STAT_SAMPLING_RATE + * To avoid that userspace shoots itself. +*/ +static unsigned int minimum_sampling_rate(void) +{ + return max(def_sampling_rate / 10, MIN_STAT_SAMPLING_RATE); +} + +/* This will also vanish soon with removing sampling_rate_max */ #define MAX_SAMPLING_RATE (500 * def_sampling_rate) -#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000) +#define LATENCY_MULTIPLIER (1000) #define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) static void do_dbs_timer(struct work_struct *work); @@ -255,13 +267,11 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused, ret = sscanf(buf, "%u", &input); mutex_lock(&dbs_mutex); - if (ret != 1 || input > MAX_SAMPLING_RATE - || input < MIN_SAMPLING_RATE) { + if (ret != 1) { mutex_unlock(&dbs_mutex); return -EINVAL; } - - dbs_tuners_ins.sampling_rate = input; + dbs_tuners_ins.sampling_rate = max(input, minimum_sampling_rate()); mutex_unlock(&dbs_mutex); return count; @@ -607,11 +617,9 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, if (latency == 0) latency = 1; - def_sampling_rate = latency * - DEF_SAMPLING_RATE_LATENCY_MULTIPLIER; - - if (def_sampling_rate < MIN_STAT_SAMPLING_RATE) - def_sampling_rate = MIN_STAT_SAMPLING_RATE; + def_sampling_rate = + max(latency * LATENCY_MULTIPLIER, + MIN_STAT_SAMPLING_RATE); dbs_tuners_ins.sampling_rate = def_sampling_rate; } -- cgit v1.2.3-59-g8ed1b From 9e5f6cf5f755ca5c52071c317421fae19966a658 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Wed, 25 Feb 2009 11:30:45 +0100 Subject: x86: update description for memtest boot parameter Signed-off-by: Andreas Herrmann Signed-off-by: Ingo Molnar --- Documentation/kernel-parameters.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index f6d5d5b9b2b1..10c4b8b75c96 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1308,8 +1308,13 @@ and is between 256 and 4096 characters. It is defined in the file memtest= [KNL,X86] Enable memtest Format: - range: 0,4 : pattern number default : 0 + Specifies the number of memtest passes to be + performed. Each pass selects another test + pattern from a given set of patterns. Memtest + fills the memory with this pattern, validates + memory contents and reserves bad memory + regions that are detected. meye.*= [HW] Set MotionEye Camera parameters See Documentation/video4linux/meye.txt. -- cgit v1.2.3-59-g8ed1b From 930738de602d2ceb0d1c1b368fe2a8d2a974ab72 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 26 Feb 2009 09:27:20 +0100 Subject: sound: virtuoso: add Xonar Essence STX support Add support for the Asus Xonar Essence STX sound card. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/ALSA-Configuration.txt | 2 +- sound/pci/Kconfig | 3 +- sound/pci/oxygen/virtuoso.c | 192 ++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 841a9365d5fd..1356d2a6772b 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -1824,7 +1824,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. ------------------- Module for sound cards based on the Asus AV100/AV200 chips, - i.e., Xonar D1, DX, D2, D2X and HDAV1.3 (Deluxe). + i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), and Essence STX. This module supports autoprobe and multiple cards. diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 82b9bddcdcd6..21d117ada84b 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -744,7 +744,8 @@ config SND_VIRTUOSO select SND_OXYGEN_LIB help Say Y here to include support for sound cards based on the - Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2 and D2X. + Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, and + Essence STX. Support for the HDAV1.3 (Deluxe) is very experimental. To compile this driver as a module, choose M here: the module diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 00dc97806f1b..bc5ce11c8b14 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -112,6 +112,34 @@ * CS4362A: AD0 <- 0 */ +/* + * Xonar Essence STX + * ----------------- + * + * CMI8788: + * + * I²C <-> PCM1792A + * + * GPI 0 <- external power present + * + * GPIO 0 -> enable output to speakers + * GPIO 1 -> route HP to front panel (0) or rear jack (1) + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 7 -> route output to speaker jacks (0) or HP (1) + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) + * + * PCM1792A: + * + * AD0 <- 0 + * + * H6 daughterboard + * ---------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 0 + */ + #include #include #include @@ -152,6 +180,7 @@ enum { MODEL_DX, MODEL_HDAV, /* without daughterboard */ MODEL_HDAV_H6, /* with H6 daughterboard */ + MODEL_STX, }; static struct pci_device_id xonar_ids[] __devinitdata = { @@ -160,6 +189,7 @@ static struct pci_device_id xonar_ids[] __devinitdata = { { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X }, { OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV }, { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 }, + { OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, { } }; @@ -184,6 +214,9 @@ MODULE_DEVICE_TABLE(pci, xonar_ids); #define GPIO_HDAV_DB_H6 0x0000 #define GPIO_HDAV_DB_XX 0x0020 +#define GPIO_ST_HP_REAR 0x0002 +#define GPIO_ST_HP 0x0080 + #define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ADx=i, /W=0 */ #define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */ #define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */ @@ -497,6 +530,36 @@ static void xonar_hdav_init(struct oxygen *chip) snd_component_add(chip->card, "CS5381"); } +static void xonar_stx_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + data->anti_pop_delay = 100; + data->dacs = 1; + data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; + data->ext_power_reg = OXYGEN_GPI_DATA; + data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->ext_power_bit = GPI_DX_EXT_POWER; + data->pcm1796_oversampling = PCM1796_OS_64; + + pcm1796_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, + GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); + + xonar_common_init(chip); + + snd_component_add(chip->card, "PCM1792A"); + snd_component_add(chip->card, "CS5381"); +} + static void xonar_disable_output(struct oxygen *chip) { struct xonar_data *data = chip->model_data; @@ -524,6 +587,11 @@ static void xonar_hdav_cleanup(struct oxygen *chip) xonar_disable_output(chip); } +static void xonar_st_cleanup(struct oxygen *chip) +{ + xonar_disable_output(chip); +} + static void xonar_d2_suspend(struct oxygen *chip) { xonar_d2_cleanup(chip); @@ -540,6 +608,11 @@ static void xonar_hdav_suspend(struct oxygen *chip) msleep(2); } +static void xonar_st_suspend(struct oxygen *chip) +{ + xonar_st_cleanup(chip); +} + static void xonar_d2_resume(struct oxygen *chip) { pcm1796_init(chip); @@ -567,6 +640,12 @@ static void xonar_hdav_resume(struct oxygen *chip) xonar_enable_output(chip); } +static void xonar_st_resume(struct oxygen *chip) +{ + pcm1796_init(chip); + xonar_enable_output(chip); +} + static void xonar_hdav_pcm_hardware_filter(unsigned int channel, struct snd_pcm_hardware *hardware) { @@ -746,6 +825,72 @@ static const struct snd_kcontrol_new front_panel_switch = { .private_value = GPIO_DX_FRONT_PANEL, }; +static int st_output_switch_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Speakers", "Headphones", "FP Headphones" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 3; + if (info->value.enumerated.item >= 3) + info->value.enumerated.item = 2; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int st_output_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 gpio; + + gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (!(gpio & GPIO_ST_HP)) + value->value.enumerated.item[0] = 0; + else if (gpio & GPIO_ST_HP_REAR) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + return 0; +} + + +static int st_output_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 gpio_old, gpio; + + mutex_lock(&chip->mutex); + gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA); + gpio = gpio_old; + switch (value->value.enumerated.item[0]) { + case 0: + gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR); + break; + case 1: + gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR; + break; + case 2: + gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR; + break; + } + oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio); + mutex_unlock(&chip->mutex); + return gpio != gpio_old; +} + +static const struct snd_kcontrol_new st_output_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output", + .info = st_output_switch_info, + .get = st_output_switch_get, + .put = st_output_switch_put, +}; + static void xonar_line_mic_ac97_switch(struct oxygen *chip, unsigned int reg, unsigned int mute) { @@ -776,6 +921,15 @@ static int xonar_d1_control_filter(struct snd_kcontrol_new *template) return 0; } +static int xonar_st_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + return 1; /* no CD input */ + if (!strcmp(template->name, "Stereo Upmixing")) + return 1; /* stereo only - we don't need upmixing */ + return 0; +} + static int xonar_d2_mixer_init(struct oxygen *chip) { return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); @@ -786,6 +940,11 @@ static int xonar_d1_mixer_init(struct oxygen *chip) return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); } +static int xonar_st_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip)); +} + static const struct oxygen_model model_xonar_d2 = { .longname = "Asus Virtuoso 200", .chip = "AV200", @@ -872,6 +1031,33 @@ static const struct oxygen_model model_xonar_hdav = { .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; +static const struct oxygen_model model_xonar_st = { + .longname = "Asus Virtuoso 100", + .chip = "AV200", + .init = xonar_stx_init, + .control_filter = xonar_st_control_filter, + .mixer_init = xonar_st_mixer_init, + .cleanup = xonar_st_cleanup, + .suspend = xonar_st_suspend, + .resume = xonar_st_resume, + .set_dac_params = set_pcm1796_params, + .set_adc_params = set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .ac97_switch = xonar_line_mic_ac97_switch, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_data), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2, + .dac_channels = 2, + .dac_volume_min = 255 - 2*60, + .dac_volume_max = 255, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + static int __devinit get_xonar_model(struct oxygen *chip, const struct pci_device_id *id) { @@ -881,6 +1067,7 @@ static int __devinit get_xonar_model(struct oxygen *chip, [MODEL_D2] = &model_xonar_d2, [MODEL_D2X] = &model_xonar_d2, [MODEL_HDAV] = &model_xonar_hdav, + [MODEL_STX] = &model_xonar_st, }; static const char *const names[] = { [MODEL_D1] = "Xonar D1", @@ -889,6 +1076,7 @@ static int __devinit get_xonar_model(struct oxygen *chip, [MODEL_D2X] = "Xonar D2X", [MODEL_HDAV] = "Xonar HDAV1.3", [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6", + [MODEL_STX] = "Xonar Essence STX", }; unsigned int model = id->driver_data; @@ -916,6 +1104,10 @@ static int __devinit get_xonar_model(struct oxygen *chip, return -ENODEV; } break; + case MODEL_STX: + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_HDAV_DB_MASK); + break; } chip->model.shortname = names[model]; -- cgit v1.2.3-59-g8ed1b From 1607b8ea0a4cc20752978fadb027daafc8a2d93c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 26 Feb 2009 16:50:43 +0100 Subject: ALSA: hda - Add model=auto for STAC/IDT codecs Added the model=auto to STAC/IDT codecs to use the BIOS default setup explicitly. It can be used to disable the device-specific model quirk in the driver. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 8 ++++++++ sound/pci/hda/patch_sigmatel.c | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 0e52d273ce96..a448bbefd488 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -280,6 +280,7 @@ STAC9200 gateway-m4 Gateway laptops with EAPD control gateway-m4-2 Gateway laptops with EAPD control panasonic Panasonic CF-74 + auto BIOS setup (default) STAC9205/9254 ============= @@ -288,6 +289,7 @@ STAC9205/9254 dell-m43 Dell Precision dell-m44 Dell Inspiron eapd Keep EAPD on (e.g. Gateway T1616) + auto BIOS setup (default) STAC9220/9221 ============= @@ -311,6 +313,7 @@ STAC9220/9221 dell-d82 Dell (unknown) dell-m81 Dell (unknown) dell-m82 Dell XPS M1210 + auto BIOS setup (default) STAC9202/9250/9251 ================== @@ -322,6 +325,7 @@ STAC9202/9250/9251 m3 Some Gateway MX series laptops m5 Some Gateway MX series laptops (MP6954) m6 Some Gateway NX series laptops + auto BIOS setup (default) STAC9227/9228/9229/927x ======================= @@ -331,6 +335,7 @@ STAC9227/9228/9229/927x 5stack D965 5stack + SPDIF dell-3stack Dell Dimension E520 dell-bios Fixes with Dell BIOS setup + auto BIOS setup (default) STAC92HD71B* ============ @@ -339,6 +344,7 @@ STAC92HD71B* dell-m4-2 Dell desktops dell-m4-3 Dell desktops hp-m4 HP dv laptops + auto BIOS setup (default) STAC92HD73* =========== @@ -348,11 +354,13 @@ STAC92HD73* dell-m6-dmic Dell desktops/laptops with digital mics dell-m6 Dell desktops/laptops with both type of mics dell-eq Dell desktops/laptops + auto BIOS setup (default) STAC92HD83* =========== ref Reference board mic-ref Reference board with power managment for ports + auto BIOS setup (default) STAC9872 ======== diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index da48d8c0b295..37ffd96a9ff2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -43,6 +43,7 @@ enum { }; enum { + STAC_AUTO, STAC_REF, STAC_9200_OQO, STAC_9200_DELL_D21, @@ -62,6 +63,7 @@ enum { }; enum { + STAC_9205_AUTO, STAC_9205_REF, STAC_9205_DELL_M42, STAC_9205_DELL_M43, @@ -71,6 +73,7 @@ enum { }; enum { + STAC_92HD73XX_AUTO, STAC_92HD73XX_NO_JD, /* no jack-detection */ STAC_92HD73XX_REF, STAC_DELL_M6_AMIC, @@ -81,6 +84,7 @@ enum { }; enum { + STAC_92HD83XXX_AUTO, STAC_92HD83XXX_REF, STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, @@ -88,6 +92,7 @@ enum { }; enum { + STAC_92HD71BXX_AUTO, STAC_92HD71BXX_REF, STAC_DELL_M4_1, STAC_DELL_M4_2, @@ -98,6 +103,7 @@ enum { }; enum { + STAC_925x_AUTO, STAC_925x_REF, STAC_M1, STAC_M1_2, @@ -110,6 +116,7 @@ enum { }; enum { + STAC_922X_AUTO, STAC_D945_REF, STAC_D945GTP3, STAC_D945GTP5, @@ -137,6 +144,7 @@ enum { }; enum { + STAC_927X_AUTO, STAC_D965_REF_NO_JD, /* no jack-detection */ STAC_D965_REF, STAC_D965_3ST, @@ -1488,6 +1496,7 @@ static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { }; static const char *stac9200_models[STAC_9200_MODELS] = { + [STAC_AUTO] = "auto", [STAC_REF] = "ref", [STAC_9200_OQO] = "oqo", [STAC_9200_DELL_D21] = "dell-d21", @@ -1633,6 +1642,7 @@ static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = { }; static const char *stac925x_models[STAC_925x_MODELS] = { + [STAC_925x_AUTO] = "auto", [STAC_REF] = "ref", [STAC_M1] = "m1", [STAC_M1_2] = "m1-2", @@ -1692,6 +1702,7 @@ static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { }; static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_AUTO] = "auto", [STAC_92HD73XX_NO_JD] = "no-jd", [STAC_92HD73XX_REF] = "ref", [STAC_DELL_M6_AMIC] = "dell-m6-amic", @@ -1748,6 +1759,7 @@ static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { }; static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { + [STAC_92HD83XXX_AUTO] = "auto", [STAC_92HD83XXX_REF] = "ref", [STAC_92HD83XXX_PWR_REF] = "mic-ref", [STAC_DELL_S14] = "dell-s14", @@ -1802,6 +1814,7 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { }; static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_AUTO] = "auto", [STAC_92HD71BXX_REF] = "ref", [STAC_DELL_M4_1] = "dell-m4-1", [STAC_DELL_M4_2] = "dell-m4-2", @@ -1973,6 +1986,7 @@ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { }; static const char *stac922x_models[STAC_922X_MODELS] = { + [STAC_922X_AUTO] = "auto", [STAC_D945_REF] = "ref", [STAC_D945GTP5] = "5stack", [STAC_D945GTP3] = "3stack", @@ -2125,6 +2139,7 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { }; static const char *stac927x_models[STAC_927X_MODELS] = { + [STAC_927X_AUTO] = "auto", [STAC_D965_REF_NO_JD] = "ref-no-jd", [STAC_D965_REF] = "ref", [STAC_D965_3ST] = "3stack", @@ -2222,6 +2237,7 @@ static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { }; static const char *stac9205_models[STAC_9205_MODELS] = { + [STAC_9205_AUTO] = "auto", [STAC_9205_REF] = "ref", [STAC_9205_DELL_M42] = "dell-m42", [STAC_9205_DELL_M43] = "dell-m43", -- cgit v1.2.3-59-g8ed1b From 0c5f9b8830aa0ff8f97e4efdfe1e1c4fe08ec71c Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Tue, 24 Feb 2009 15:30:38 +0000 Subject: RDS: Documentation This file documents the specifics of the RDS sockets API, as well as covering some of the details of its internal implementation. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- Documentation/networking/rds.txt | 356 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 Documentation/networking/rds.txt (limited to 'Documentation') diff --git a/Documentation/networking/rds.txt b/Documentation/networking/rds.txt new file mode 100644 index 000000000000..c67077cbeb80 --- /dev/null +++ b/Documentation/networking/rds.txt @@ -0,0 +1,356 @@ + +Overview +======== + +This readme tries to provide some background on the hows and whys of RDS, +and will hopefully help you find your way around the code. + +In addition, please see this email about RDS origins: +http://oss.oracle.com/pipermail/rds-devel/2007-November/000228.html + +RDS Architecture +================ + +RDS provides reliable, ordered datagram delivery by using a single +reliable connection between any two nodes in the cluster. This allows +applications to use a single socket to talk to any other process in the +cluster - so in a cluster with N processes you need N sockets, in contrast +to N*N if you use a connection-oriented socket transport like TCP. + +RDS is not Infiniband-specific; it was designed to support different +transports. The current implementation used to support RDS over TCP as well +as IB. Work is in progress to support RDS over iWARP, and using DCE to +guarantee no dropped packets on Ethernet, it may be possible to use RDS over +UDP in the future. + +The high-level semantics of RDS from the application's point of view are + + * Addressing + RDS uses IPv4 addresses and 16bit port numbers to identify + the end point of a connection. All socket operations that involve + passing addresses between kernel and user space generally + use a struct sockaddr_in. + + The fact that IPv4 addresses are used does not mean the underlying + transport has to be IP-based. In fact, RDS over IB uses a + reliable IB connection; the IP address is used exclusively to + locate the remote node's GID (by ARPing for the given IP). + + The port space is entirely independent of UDP, TCP or any other + protocol. + + * Socket interface + RDS sockets work *mostly* as you would expect from a BSD + socket. The next section will cover the details. At any rate, + all I/O is performed through the standard BSD socket API. + Some additions like zerocopy support are implemented through + control messages, while other extensions use the getsockopt/ + setsockopt calls. + + Sockets must be bound before you can send or receive data. + This is needed because binding also selects a transport and + attaches it to the socket. Once bound, the transport assignment + does not change. RDS will tolerate IPs moving around (eg in + a active-active HA scenario), but only as long as the address + doesn't move to a different transport. + + * sysctls + RDS supports a number of sysctls in /proc/sys/net/rds + + +Socket Interface +================ + + AF_RDS, PF_RDS, SOL_RDS + These constants haven't been assigned yet, because RDS isn't in + mainline yet. Currently, the kernel module assigns some constant + and publishes it to user space through two sysctl files + /proc/sys/net/rds/pf_rds + /proc/sys/net/rds/sol_rds + + fd = socket(PF_RDS, SOCK_SEQPACKET, 0); + This creates a new, unbound RDS socket. + + setsockopt(SOL_SOCKET): send and receive buffer size + RDS honors the send and receive buffer size socket options. + You are not allowed to queue more than SO_SNDSIZE bytes to + a socket. A message is queued when sendmsg is called, and + it leaves the queue when the remote system acknowledges + its arrival. + + The SO_RCVSIZE option controls the maximum receive queue length. + This is a soft limit rather than a hard limit - RDS will + continue to accept and queue incoming messages, even if that + takes the queue length over the limit. However, it will also + mark the port as "congested" and send a congestion update to + the source node. The source node is supposed to throttle any + processes sending to this congested port. + + bind(fd, &sockaddr_in, ...) + This binds the socket to a local IP address and port, and a + transport. + + sendmsg(fd, ...) + Sends a message to the indicated recipient. The kernel will + transparently establish the underlying reliable connection + if it isn't up yet. + + An attempt to send a message that exceeds SO_SNDSIZE will + return with -EMSGSIZE + + An attempt to send a message that would take the total number + of queued bytes over the SO_SNDSIZE threshold will return + EAGAIN. + + An attempt to send a message to a destination that is marked + as "congested" will return ENOBUFS. + + recvmsg(fd, ...) + Receives a message that was queued to this socket. The sockets + recv queue accounting is adjusted, and if the queue length + drops below SO_SNDSIZE, the port is marked uncongested, and + a congestion update is sent to all peers. + + Applications can ask the RDS kernel module to receive + notifications via control messages (for instance, there is a + notification when a congestion update arrived, or when a RDMA + operation completes). These notifications are received through + the msg.msg_control buffer of struct msghdr. The format of the + messages is described in manpages. + + poll(fd) + RDS supports the poll interface to allow the application + to implement async I/O. + + POLLIN handling is pretty straightforward. When there's an + incoming message queued to the socket, or a pending notification, + we signal POLLIN. + + POLLOUT is a little harder. Since you can essentially send + to any destination, RDS will always signal POLLOUT as long as + there's room on the send queue (ie the number of bytes queued + is less than the sendbuf size). + + However, the kernel will refuse to accept messages to + a destination marked congested - in this case you will loop + forever if you rely on poll to tell you what to do. + This isn't a trivial problem, but applications can deal with + this - by using congestion notifications, and by checking for + ENOBUFS errors returned by sendmsg. + + setsockopt(SOL_RDS, RDS_CANCEL_SENT_TO, &sockaddr_in) + This allows the application to discard all messages queued to a + specific destination on this particular socket. + + This allows the application to cancel outstanding messages if + it detects a timeout. For instance, if it tried to send a message, + and the remote host is unreachable, RDS will keep trying forever. + The application may decide it's not worth it, and cancel the + operation. In this case, it would use RDS_CANCEL_SENT_TO to + nuke any pending messages. + + +RDMA for RDS +============ + + see rds-rdma(7) manpage (available in rds-tools) + + +Congestion Notifications +======================== + + see rds(7) manpage + + +RDS Protocol +============ + + Message header + + The message header is a 'struct rds_header' (see rds.h): + Fields: + h_sequence: + per-packet sequence number + h_ack: + piggybacked acknowledgment of last packet received + h_len: + length of data, not including header + h_sport: + source port + h_dport: + destination port + h_flags: + CONG_BITMAP - this is a congestion update bitmap + ACK_REQUIRED - receiver must ack this packet + RETRANSMITTED - packet has previously been sent + h_credit: + indicate to other end of connection that + it has more credits available (i.e. there is + more send room) + h_padding[4]: + unused, for future use + h_csum: + header checksum + h_exthdr: + optional data can be passed here. This is currently used for + passing RDMA-related information. + + ACK and retransmit handling + + One might think that with reliable IB connections you wouldn't need + to ack messages that have been received. The problem is that IB + hardware generates an ack message before it has DMAed the message + into memory. This creates a potential message loss if the HCA is + disabled for any reason between when it sends the ack and before + the message is DMAed and processed. This is only a potential issue + if another HCA is available for fail-over. + + Sending an ack immediately would allow the sender to free the sent + message from their send queue quickly, but could cause excessive + traffic to be used for acks. RDS piggybacks acks on sent data + packets. Ack-only packets are reduced by only allowing one to be + in flight at a time, and by the sender only asking for acks when + its send buffers start to fill up. All retransmissions are also + acked. + + Flow Control + + RDS's IB transport uses a credit-based mechanism to verify that + there is space in the peer's receive buffers for more data. This + eliminates the need for hardware retries on the connection. + + Congestion + + Messages waiting in the receive queue on the receiving socket + are accounted against the sockets SO_RCVBUF option value. Only + the payload bytes in the message are accounted for. If the + number of bytes queued equals or exceeds rcvbuf then the socket + is congested. All sends attempted to this socket's address + should return block or return -EWOULDBLOCK. + + Applications are expected to be reasonably tuned such that this + situation very rarely occurs. An application encountering this + "back-pressure" is considered a bug. + + This is implemented by having each node maintain bitmaps which + indicate which ports on bound addresses are congested. As the + bitmap changes it is sent through all the connections which + terminate in the local address of the bitmap which changed. + + The bitmaps are allocated as connections are brought up. This + avoids allocation in the interrupt handling path which queues + sages on sockets. The dense bitmaps let transports send the + entire bitmap on any bitmap change reasonably efficiently. This + is much easier to implement than some finer-grained + communication of per-port congestion. The sender does a very + inexpensive bit test to test if the port it's about to send to + is congested or not. + + +RDS Transport Layer +================== + + As mentioned above, RDS is not IB-specific. Its code is divided + into a general RDS layer and a transport layer. + + The general layer handles the socket API, congestion handling, + loopback, stats, usermem pinning, and the connection state machine. + + The transport layer handles the details of the transport. The IB + transport, for example, handles all the queue pairs, work requests, + CM event handlers, and other Infiniband details. + + +RDS Kernel Structures +===================== + + struct rds_message + aka possibly "rds_outgoing", the generic RDS layer copies data to + be sent and sets header fields as needed, based on the socket API. + This is then queued for the individual connection and sent by the + connection's transport. + struct rds_incoming + a generic struct referring to incoming data that can be handed from + the transport to the general code and queued by the general code + while the socket is awoken. It is then passed back to the transport + code to handle the actual copy-to-user. + struct rds_socket + per-socket information + struct rds_connection + per-connection information + struct rds_transport + pointers to transport-specific functions + struct rds_statistics + non-transport-specific statistics + struct rds_cong_map + wraps the raw congestion bitmap, contains rbnode, waitq, etc. + +Connection management +===================== + + Connections may be in UP, DOWN, CONNECTING, DISCONNECTING, and + ERROR states. + + The first time an attempt is made by an RDS socket to send data to + a node, a connection is allocated and connected. That connection is + then maintained forever -- if there are transport errors, the + connection will be dropped and re-established. + + Dropping a connection while packets are queued will cause queued or + partially-sent datagrams to be retransmitted when the connection is + re-established. + + +The send path +============= + + rds_sendmsg() + struct rds_message built from incoming data + CMSGs parsed (e.g. RDMA ops) + transport connection alloced and connected if not already + rds_message placed on send queue + send worker awoken + rds_send_worker() + calls rds_send_xmit() until queue is empty + rds_send_xmit() + transmits congestion map if one is pending + may set ACK_REQUIRED + calls transport to send either non-RDMA or RDMA message + (RDMA ops never retransmitted) + rds_ib_xmit() + allocs work requests from send ring + adds any new send credits available to peer (h_credits) + maps the rds_message's sg list + piggybacks ack + populates work requests + post send to connection's queue pair + +The recv path +============= + + rds_ib_recv_cq_comp_handler() + looks at write completions + unmaps recv buffer from device + no errors, call rds_ib_process_recv() + refill recv ring + rds_ib_process_recv() + validate header checksum + copy header to rds_ib_incoming struct if start of a new datagram + add to ibinc's fraglist + if competed datagram: + update cong map if datagram was cong update + call rds_recv_incoming() otherwise + note if ack is required + rds_recv_incoming() + drop duplicate packets + respond to pings + find the sock associated with this datagram + add to sock queue + wake up sock + do some congestion calculations + rds_recvmsg + copy data into user iovec + handle CMSGs + return to application + + -- cgit v1.2.3-59-g8ed1b From 72fd455ba54b5a02b9c74221b9ded8b1845b464a Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Mon, 2 Mar 2009 13:55:14 +0800 Subject: sched, documentation: remove old O(1) scheduler document Since we don't have O(1) scheduler implementation anymore, remove the legacy doc. Signed-off-by: Wang Chen Signed-off-by: Ingo Molnar --- Documentation/scheduler/00-INDEX | 2 - Documentation/scheduler/sched-coding.txt | 126 ------------------------------- 2 files changed, 128 deletions(-) delete mode 100644 Documentation/scheduler/sched-coding.txt (limited to 'Documentation') diff --git a/Documentation/scheduler/00-INDEX b/Documentation/scheduler/00-INDEX index aabcc3a089ba..3c00c9c3219e 100644 --- a/Documentation/scheduler/00-INDEX +++ b/Documentation/scheduler/00-INDEX @@ -2,8 +2,6 @@ - this file. sched-arch.txt - CPU Scheduler implementation hints for architecture specific code. -sched-coding.txt - - reference for various scheduler-related methods in the O(1) scheduler. sched-design-CFS.txt - goals, design and implementation of the Complete Fair Scheduler. sched-domains.txt diff --git a/Documentation/scheduler/sched-coding.txt b/Documentation/scheduler/sched-coding.txt deleted file mode 100644 index cbd8db752acf..000000000000 --- a/Documentation/scheduler/sched-coding.txt +++ /dev/null @@ -1,126 +0,0 @@ - Reference for various scheduler-related methods in the O(1) scheduler - Robert Love , MontaVista Software - - -Note most of these methods are local to kernel/sched.c - this is by design. -The scheduler is meant to be self-contained and abstracted away. This document -is primarily for understanding the scheduler, not interfacing to it. Some of -the discussed interfaces, however, are general process/scheduling methods. -They are typically defined in include/linux/sched.h. - - -Main Scheduling Methods ------------------------ - -void load_balance(runqueue_t *this_rq, int idle) - Attempts to pull tasks from one cpu to another to balance cpu usage, - if needed. This method is called explicitly if the runqueues are - imbalanced or periodically by the timer tick. Prior to calling, - the current runqueue must be locked and interrupts disabled. - -void schedule() - The main scheduling function. Upon return, the highest priority - process will be active. - - -Locking -------- - -Each runqueue has its own lock, rq->lock. When multiple runqueues need -to be locked, lock acquires must be ordered by ascending &runqueue value. - -A specific runqueue is locked via - - task_rq_lock(task_t pid, unsigned long *flags) - -which disables preemption, disables interrupts, and locks the runqueue pid is -running on. Likewise, - - task_rq_unlock(task_t pid, unsigned long *flags) - -unlocks the runqueue pid is running on, restores interrupts to their previous -state, and reenables preemption. - -The routines - - double_rq_lock(runqueue_t *rq1, runqueue_t *rq2) - -and - - double_rq_unlock(runqueue_t *rq1, runqueue_t *rq2) - -safely lock and unlock, respectively, the two specified runqueues. They do -not, however, disable and restore interrupts. Users are required to do so -manually before and after calls. - - -Values ------- - -MAX_PRIO - The maximum priority of the system, stored in the task as task->prio. - Lower priorities are higher. Normal (non-RT) priorities range from - MAX_RT_PRIO to (MAX_PRIO - 1). -MAX_RT_PRIO - The maximum real-time priority of the system. Valid RT priorities - range from 0 to (MAX_RT_PRIO - 1). -MAX_USER_RT_PRIO - The maximum real-time priority that is exported to user-space. Should - always be equal to or less than MAX_RT_PRIO. Setting it less allows - kernel threads to have higher priorities than any user-space task. -MIN_TIMESLICE -MAX_TIMESLICE - Respectively, the minimum and maximum timeslices (quanta) of a process. - -Data ----- - -struct runqueue - The main per-CPU runqueue data structure. -struct task_struct - The main per-process data structure. - - -General Methods ---------------- - -cpu_rq(cpu) - Returns the runqueue of the specified cpu. -this_rq() - Returns the runqueue of the current cpu. -task_rq(pid) - Returns the runqueue which holds the specified pid. -cpu_curr(cpu) - Returns the task currently running on the given cpu. -rt_task(pid) - Returns true if pid is real-time, false if not. - - -Process Control Methods ------------------------ - -void set_user_nice(task_t *p, long nice) - Sets the "nice" value of task p to the given value. -int setscheduler(pid_t pid, int policy, struct sched_param *param) - Sets the scheduling policy and parameters for the given pid. -int set_cpus_allowed(task_t *p, unsigned long new_mask) - Sets a given task's CPU affinity and migrates it to a proper cpu. - Callers must have a valid reference to the task and assure the - task not exit prematurely. No locks can be held during the call. -set_task_state(tsk, state_value) - Sets the given task's state to the given value. -set_current_state(state_value) - Sets the current task's state to the given value. -void set_tsk_need_resched(struct task_struct *tsk) - Sets need_resched in the given task. -void clear_tsk_need_resched(struct task_struct *tsk) - Clears need_resched in the given task. -void set_need_resched() - Sets need_resched in the current task. -void clear_need_resched() - Clears need_resched in the current task. -int need_resched() - Returns true if need_resched is set in the current task, false - otherwise. -yield() - Place the current process at the end of the runqueue and call schedule. -- cgit v1.2.3-59-g8ed1b From d02b1f3910f12cfe377a31afebcbbde4f5664b74 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Mar 2009 17:34:51 +0100 Subject: ALSA: hda - Update documetation for hints sysfs entry Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index 99958be7b454..c5948f2f9a25 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -365,10 +365,13 @@ modelname:: to this file. init_verbs:: The extra verbs to execute at initialization. You can add a verb by - writing to this file. Pass three numbers: nid, verb and parameter. + writing to this file. Pass three numbers: nid, verb and parameter + (separated with a space). hints:: - Shows hint strings for codec parsers for any use. Right now it's - not used. + Shows / stores hint strings for codec parsers for any use. + Its format is `key = value`. For example, passing `hp_detect = yes` + to IDT/STAC codec parser will result in the disablement of the + headphone detection. init_pin_configs:: Shows the initial pin default config values set by BIOS. driver_pin_configs:: -- cgit v1.2.3-59-g8ed1b From 79d7d5333b598e9a559bf27833f0ad2b8bf6ad2c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 4 Mar 2009 09:03:50 +0100 Subject: ALSA: hda - Fix HP dv6736 mic input Fix the mic input of HP dv6736 with Conexant 5051 codec chip. This laptop seems have no mic-switching per jack connection. A new model hp-dv6736 is introduced to match with the h/w implementation. Reference: Novell bnc#480753 https://bugzilla.novell.com/show_bug.cgi?id=480753 Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 1 + sound/pci/hda/patch_conexant.c | 63 +++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index a448bbefd488..80b796e4a80e 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -262,6 +262,7 @@ Conexant 5051 ============= laptop Basic Laptop config (default) hp HP Spartan laptop + hp-dv6736 HP dv6736 lenovo-x200 Lenovo X200 laptop STAC9200 diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index b8de73ecfdeb..1938e92e1f03 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -72,6 +72,7 @@ struct conexant_spec { */ unsigned int cur_eapd; unsigned int hp_present; + unsigned int no_auto_mic; unsigned int need_dac_fix; /* capture */ @@ -1665,8 +1666,11 @@ static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol, /* toggle input of built-in and mic jack appropriately */ static void cxt5051_portb_automic(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; unsigned int present; + if (spec->no_auto_mic) + return; present = snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE; @@ -1682,6 +1686,8 @@ static void cxt5051_portc_automic(struct hda_codec *codec) unsigned int present; hda_nid_t new_adc; + if (spec->no_auto_mic) + return; present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE; @@ -1768,6 +1774,22 @@ static struct snd_kcontrol_new cxt5051_hp_mixers[] = { {} }; +static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { + HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5051_hp_master_sw_put, + .private_value = 0x1a, + }, + + {} +}; + static struct hda_verb cxt5051_init_verbs[] = { /* Line in, Mic */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, @@ -1798,6 +1820,32 @@ static struct hda_verb cxt5051_init_verbs[] = { { } /* end */ }; +static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = { + /* Line in, Mic */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, + /* SPK */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP, Amp */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Record selector: Int mic */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* SPDIF route: PCM */ + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* EAPD */ + {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, + { } /* end */ +}; + static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { /* Line in, Mic */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, @@ -1849,6 +1897,7 @@ static int cxt5051_init(struct hda_codec *codec) enum { CXT5051_LAPTOP, /* Laptops w/ EAPD support */ CXT5051_HP, /* no docking */ + CXT5051_HP_DV6736, /* HP without mic switch */ CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ CXT5051_MODELS }; @@ -1856,10 +1905,12 @@ enum { static const char *cxt5051_models[CXT5051_MODELS] = { [CXT5051_LAPTOP] = "laptop", [CXT5051_HP] = "hp", + [CXT5051_HP_DV6736] = "hp-dv6736", [CXT5051_LENOVO_X200] = "lenovo-x200", }; static struct snd_pci_quirk cxt5051_cfg_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736), SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), @@ -1896,20 +1947,22 @@ static int patch_cxt5051(struct hda_codec *codec) spec->cur_adc = 0; spec->cur_adc_idx = 0; + codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; + board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, cxt5051_models, cxt5051_cfg_tbl); switch (board_config) { case CXT5051_HP: - codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; spec->mixers[0] = cxt5051_hp_mixers; break; + case CXT5051_HP_DV6736: + spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs; + spec->mixers[0] = cxt5051_hp_dv6736_mixers; + spec->no_auto_mic = 1; + break; case CXT5051_LENOVO_X200: spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; - /* fallthru */ - default: - case CXT5051_LAPTOP: - codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; break; } -- cgit v1.2.3-59-g8ed1b From 3197ebdb130473a92760100cbfe0d7e671838f48 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 31 Mar 2009 09:10:09 -0400 Subject: ext4: Add sysfs support Add basic sysfs support so that information about the mounted filesystem and various tuning parameters can be accessed via /sys/fs/ext4//*. Signed-off-by: "Theodore Ts'o" --- Documentation/ABI/testing/sysfs-fs-ext4 | 81 +++++++++++++ fs/ext4/ext4_sb.h | 2 + fs/ext4/super.c | 207 ++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-fs-ext4 (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-fs-ext4 b/Documentation/ABI/testing/sysfs-fs-ext4 new file mode 100644 index 000000000000..4e79074de282 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-fs-ext4 @@ -0,0 +1,81 @@ +What: /sys/fs/ext4//mb_stats +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + Controls whether the multiblock allocator should + collect statistics, which are shown during the unmount. + 1 means to collect statistics, 0 means not to collect + statistics + +What: /sys/fs/ext4//mb_group_prealloc +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + The multiblock allocator will round up allocation + requests to a multiple of this tuning parameter if the + stripe size is not set in the ext4 superblock + +What: /sys/fs/ext4//mb_max_to_scan +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + The maximum number of extents the multiblock allocator + will search to find the best extent + +What: /sys/fs/ext4//mb_min_to_scan +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + The minimum number of extents the multiblock allocator + will search to find the best extent + +What: /sys/fs/ext4//mb_order2_req +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + Tuning parameter which controls the minimum size for + requests (as a power of 2) where the buddy cache is + used + +What: /sys/fs/ext4//mb_stream_req +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + Files which have fewer blocks than this tunable + parameter will have their blocks allocated out of a + block group specific preallocation pool, so that small + files are packed closely together. Each large file + will have its blocks allocated out of its own unique + preallocation pool. + +What: /sys/fs/ext4//inode_readahead +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + Tuning parameter which controls the maximum number of + inode table blocks that ext4's inode table readahead + algorithm will pre-read into the buffer cache + +What: /sys/fs/ext4//delayed_allocation_blocks +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + This file is read-only and shows the number of blocks + that are dirty in the page cache, but which do not + have their location in the filesystem allocated yet. + +What: /sys/fs/ext4//lifetime_write_kbytes +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + This file is read-only and shows the number of kilobytes + of data that have been written to this filesystem since it was + created. + +What: /sys/fs/ext4//session_write_kbytes +Date: March 2008 +Contact: "Theodore Ts'o" +Description: + This file is read-only and shows the number of + kilobytes of data that have been written to this + filesystem since it was mounted. diff --git a/fs/ext4/ext4_sb.h b/fs/ext4/ext4_sb.h index 50ab1169c378..57b71fefbccf 100644 --- a/fs/ext4/ext4_sb.h +++ b/fs/ext4/ext4_sb.h @@ -64,6 +64,8 @@ struct ext4_sb_info { struct percpu_counter s_dirtyblocks_counter; struct blockgroup_lock *s_blockgroup_lock; struct proc_dir_entry *s_proc; + struct kobject s_kobj; + struct completion s_kobj_unregister; /* Journaling */ struct inode *s_journal_inode; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 30fc27cdf8fc..2883d4318c22 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include "group.h" struct proc_dir_entry *ext4_proc_root; +static struct kset *ext4_kset; static int ext4_load_journal(struct super_block *, struct ext4_super_block *, unsigned long journal_devnum); @@ -580,6 +582,7 @@ static void ext4_put_super(struct super_block *sb) remove_proc_entry("inode_readahead_blks", sbi->s_proc); remove_proc_entry(sb->s_id, ext4_proc_root); } + kobject_del(&sbi->s_kobj); for (i = 0; i < sbi->s_gdb_count; i++) brelse(sbi->s_group_desc[i]); @@ -615,6 +618,16 @@ static void ext4_put_super(struct super_block *sb) ext4_blkdev_remove(sbi); } sb->s_fs_info = NULL; + /* + * Now that we are completely done shutting down the + * superblock, we need to actually destroy the kobject. + */ + unlock_kernel(); + unlock_super(sb); + kobject_put(&sbi->s_kobj); + wait_for_completion(&sbi->s_kobj_unregister); + lock_super(sb); + lock_kernel(); kfree(sbi->s_blockgroup_lock); kfree(sbi); return; @@ -1464,6 +1477,11 @@ set_qf_format: return 0; if (option < 0 || option > (1 << 30)) return 0; + if (option & (option - 1)) { + printk(KERN_ERR "EXT4-fs: inode_readahead_blks" + " must be a power of 2\n"); + return 0; + } sbi->s_inode_readahead_blks = option; break; case Opt_journal_ioprio: @@ -1992,6 +2010,181 @@ static unsigned long ext4_get_stripe_size(struct ext4_sb_info *sbi) return 0; } +/* sysfs supprt */ + +struct ext4_attr { + struct attribute attr; + ssize_t (*show)(struct ext4_attr *, struct ext4_sb_info *, char *); + ssize_t (*store)(struct ext4_attr *, struct ext4_sb_info *, + const char *, size_t); + int offset; +}; + +static int parse_strtoul(const char *buf, + unsigned long max, unsigned long *value) +{ + char *endp; + + while (*buf && isspace(*buf)) + buf++; + *value = simple_strtoul(buf, &endp, 0); + while (*endp && isspace(*endp)) + endp++; + if (*endp || *value > max) + return -EINVAL; + + return 0; +} + +static ssize_t delayed_allocation_blocks_show(struct ext4_attr *a, + struct ext4_sb_info *sbi, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%llu\n", + (s64) percpu_counter_sum(&sbi->s_dirtyblocks_counter)); +} + +static ssize_t session_write_kbytes_show(struct ext4_attr *a, + struct ext4_sb_info *sbi, char *buf) +{ + struct super_block *sb = sbi->s_buddy_cache->i_sb; + + return snprintf(buf, PAGE_SIZE, "%lu\n", + (part_stat_read(sb->s_bdev->bd_part, sectors[1]) - + sbi->s_sectors_written_start) >> 1); +} + +static ssize_t lifetime_write_kbytes_show(struct ext4_attr *a, + struct ext4_sb_info *sbi, char *buf) +{ + struct super_block *sb = sbi->s_buddy_cache->i_sb; + + return snprintf(buf, PAGE_SIZE, "%llu\n", + sbi->s_kbytes_written + + ((part_stat_read(sb->s_bdev->bd_part, sectors[1]) - + EXT4_SB(sb)->s_sectors_written_start) >> 1)); +} + +static ssize_t inode_readahead_blks_store(struct ext4_attr *a, + struct ext4_sb_info *sbi, + const char *buf, size_t count) +{ + unsigned long t; + + if (parse_strtoul(buf, 0x40000000, &t)) + return -EINVAL; + + /* inode_readahead_blks must be a power of 2 */ + if (t & (t-1)) + return -EINVAL; + + sbi->s_inode_readahead_blks = t; + return count; +} + +static ssize_t sbi_ui_show(struct ext4_attr *a, + struct ext4_sb_info *sbi, char *buf) +{ + unsigned int *ui = (unsigned int *) (((char *) sbi) + a->offset); + + return snprintf(buf, PAGE_SIZE, "%u\n", *ui); +} + +static ssize_t sbi_ui_store(struct ext4_attr *a, + struct ext4_sb_info *sbi, + const char *buf, size_t count) +{ + unsigned int *ui = (unsigned int *) (((char *) sbi) + a->offset); + unsigned long t; + + if (parse_strtoul(buf, 0xffffffff, &t)) + return -EINVAL; + *ui = t; + return count; +} + +#define EXT4_ATTR_OFFSET(_name,_mode,_show,_store,_elname) \ +static struct ext4_attr ext4_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ + .offset = offsetof(struct ext4_sb_info, _elname), \ +} +#define EXT4_ATTR(name, mode, show, store) \ +static struct ext4_attr ext4_attr_##name = __ATTR(name, mode, show, store) + +#define EXT4_RO_ATTR(name) EXT4_ATTR(name, 0444, name##_show, NULL) +#define EXT4_RW_ATTR(name) EXT4_ATTR(name, 0644, name##_show, name##_store) +#define EXT4_RW_ATTR_SBI_UI(name, elname) \ + EXT4_ATTR_OFFSET(name, 0644, sbi_ui_show, sbi_ui_store, elname) +#define ATTR_LIST(name) &ext4_attr_##name.attr + +EXT4_RO_ATTR(delayed_allocation_blocks); +EXT4_RO_ATTR(session_write_kbytes); +EXT4_RO_ATTR(lifetime_write_kbytes); +EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, sbi_ui_show, + inode_readahead_blks_store, s_inode_readahead_blks); +EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats); +EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan); +EXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan); +EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs); +EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request); +EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc); + +static struct attribute *ext4_attrs[] = { + ATTR_LIST(delayed_allocation_blocks), + ATTR_LIST(session_write_kbytes), + ATTR_LIST(lifetime_write_kbytes), + ATTR_LIST(inode_readahead_blks), + ATTR_LIST(mb_stats), + ATTR_LIST(mb_max_to_scan), + ATTR_LIST(mb_min_to_scan), + ATTR_LIST(mb_order2_req), + ATTR_LIST(mb_stream_req), + ATTR_LIST(mb_group_prealloc), + NULL, +}; + +static ssize_t ext4_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info, + s_kobj); + struct ext4_attr *a = container_of(attr, struct ext4_attr, attr); + + return a->show ? a->show(a, sbi, buf) : 0; +} + +static ssize_t ext4_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info, + s_kobj); + struct ext4_attr *a = container_of(attr, struct ext4_attr, attr); + + return a->store ? a->store(a, sbi, buf, len) : 0; +} + +static void ext4_sb_release(struct kobject *kobj) +{ + struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info, + s_kobj); + complete(&sbi->s_kobj_unregister); +} + + +static struct sysfs_ops ext4_attr_ops = { + .show = ext4_attr_show, + .store = ext4_attr_store, +}; + +static struct kobj_type ext4_ktype = { + .default_attrs = ext4_attrs, + .sysfs_ops = &ext4_attr_ops, + .release = ext4_sb_release, +}; + static int ext4_fill_super(struct super_block *sb, void *data, int silent) __releases(kernel_lock) __acquires(kernel_lock) @@ -2575,6 +2768,16 @@ no_journal: goto failed_mount4; } + sbi->s_kobj.kset = ext4_kset; + init_completion(&sbi->s_kobj_unregister); + err = kobject_init_and_add(&sbi->s_kobj, &ext4_ktype, NULL, + "%s", sb->s_id); + if (err) { + ext4_mb_release(sb); + ext4_ext_release(sb); + goto failed_mount4; + }; + /* * akpm: core read_super() calls in here with the superblock locked. * That deadlocks, because orphan cleanup needs to lock the superblock @@ -3734,6 +3937,9 @@ static int __init init_ext4_fs(void) { int err; + ext4_kset = kset_create_and_add("ext4", NULL, fs_kobj); + if (!ext4_kset) + return -ENOMEM; ext4_proc_root = proc_mkdir("fs/ext4", NULL); err = init_ext4_mballoc(); if (err) @@ -3775,6 +3981,7 @@ static void __exit exit_ext4_fs(void) exit_ext4_xattr(); exit_ext4_mballoc(); remove_proc_entry("fs/ext4", NULL); + kset_unregister(ext4_kset); } MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others"); -- cgit v1.2.3-59-g8ed1b From b713a5ec55bf73c833f9883cdd761b20ee61a1ab Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 31 Mar 2009 09:11:14 -0400 Subject: ext4: remove /proc tuning knobs Remove tuning knobs in /proc/fs/ext4//*. Signed-off-by: "Theodore Ts'o" --- Documentation/filesystems/proc.txt | 21 ------- fs/ext4/ext4.h | 16 ----- fs/ext4/inode.c | 7 +-- fs/ext4/mballoc.c | 117 +++++++++---------------------------- fs/ext4/super.c | 46 --------------- 5 files changed, 30 insertions(+), 177 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 830bad7cce0f..efc4fd9f40ce 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -940,27 +940,6 @@ Table 1-10: Files in /proc/fs/ext4/ File Content mb_groups details of multiblock allocator buddy cache of free blocks mb_history multiblock allocation history - stats controls whether the multiblock allocator should start - collecting statistics, which are shown during the unmount - group_prealloc the multiblock allocator will round up allocation - requests to a multiple of this tuning parameter if the - stripe size is not set in the ext4 superblock - max_to_scan The maximum number of extents the multiblock allocator - will search to find the best extent - min_to_scan The minimum number of extents the multiblock allocator - will search to find the best extent - order2_req Tuning parameter which controls the minimum size for - requests (as a power of 2) where the buddy cache is - used - stream_req Files which have fewer blocks than this tunable - parameter will have their blocks allocated out of a - block group specific preallocation pool, so that small - files are packed closely together. Each large file - will have its blocks allocated out of its own unique - preallocation pool. -inode_readahead Tuning parameter which controls the maximum number of - inode table blocks that ext4's inode table readahead - algorithm will pre-read into the buffer cache .............................................................................. diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 0bd39188531c..e5c273ff928b 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -976,22 +976,6 @@ void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr, extern struct proc_dir_entry *ext4_proc_root; -#ifdef CONFIG_PROC_FS -extern const struct file_operations ext4_ui_proc_fops; - -#define EXT4_PROC_HANDLER(name, var) \ -do { \ - proc = proc_create_data(name, mode, sbi->s_proc, \ - &ext4_ui_proc_fops, &sbi->s_##var); \ - if (proc == NULL) { \ - printk(KERN_ERR "EXT4-fs: can't create %s\n", name); \ - goto err_out; \ - } \ -} while (0) -#else -#define EXT4_PROC_HANDLER(name, var) -#endif - /* * Function prototypes */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 7dcac9d7e491..d3118d1acc39 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4153,12 +4153,7 @@ make_io: unsigned num; table = ext4_inode_table(sb, gdp); - /* Make sure s_inode_readahead_blks is a power of 2 */ - while (EXT4_SB(sb)->s_inode_readahead_blks & - (EXT4_SB(sb)->s_inode_readahead_blks-1)) - EXT4_SB(sb)->s_inode_readahead_blks = - (EXT4_SB(sb)->s_inode_readahead_blks & - (EXT4_SB(sb)->s_inode_readahead_blks-1)); + /* s_inode_readahead_blks is always a power of 2 */ b = block & ~(EXT4_SB(sb)->s_inode_readahead_blks-1); if (table > b) b = table; diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index b0d6022eaa67..c4c430977622 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -46,22 +46,23 @@ * The allocation request involve request for multiple number of blocks * near to the goal(block) value specified. * - * During initialization phase of the allocator we decide to use the group - * preallocation or inode preallocation depending on the size file. The - * size of the file could be the resulting file size we would have after - * allocation or the current file size which ever is larger. If the size is - * less that sbi->s_mb_stream_request we select the group - * preallocation. The default value of s_mb_stream_request is 16 - * blocks. This can also be tuned via - * /proc/fs/ext4//stream_req. The value is represented in terms - * of number of blocks. + * During initialization phase of the allocator we decide to use the + * group preallocation or inode preallocation depending on the size of + * the file. The size of the file could be the resulting file size we + * would have after allocation, or the current file size, which ever + * is larger. If the size is less than sbi->s_mb_stream_request we + * select to use the group preallocation. The default value of + * s_mb_stream_request is 16 blocks. This can also be tuned via + * /sys/fs/ext4//mb_stream_req. The value is represented in + * terms of number of blocks. * * The main motivation for having small file use group preallocation is to - * ensure that we have small file closer in the disk. + * ensure that we have small files closer together on the disk. * - * First stage the allocator looks at the inode prealloc list - * ext4_inode_info->i_prealloc_list contain list of prealloc spaces for - * this particular inode. The inode prealloc space is represented as: + * First stage the allocator looks at the inode prealloc list, + * ext4_inode_info->i_prealloc_list, which contains list of prealloc + * spaces for this particular inode. The inode prealloc space is + * represented as: * * pa_lstart -> the logical start block for this prealloc space * pa_pstart -> the physical start block for this prealloc space @@ -121,29 +122,29 @@ * list. In case of inode preallocation we follow a list of heuristics * based on file size. This can be found in ext4_mb_normalize_request. If * we are doing a group prealloc we try to normalize the request to - * sbi->s_mb_group_prealloc. Default value of s_mb_group_prealloc is set to + * sbi->s_mb_group_prealloc. Default value of s_mb_group_prealloc is * 512 blocks. This can be tuned via - * /proc/fs/ext4/ option the group prealloc request is normalized to the * stripe value (sbi->s_stripe) * - * The regular allocator(using the buddy cache) support few tunables. + * The regular allocator(using the buddy cache) supports few tunables. * - * /proc/fs/ext4//min_to_scan - * /proc/fs/ext4//max_to_scan - * /proc/fs/ext4//order2_req + * /sys/fs/ext4//mb_min_to_scan + * /sys/fs/ext4//mb_max_to_scan + * /sys/fs/ext4//mb_order2_req * - * The regular allocator use buddy scan only if the request len is power of + * The regular allocator uses buddy scan only if the request len is power of * 2 blocks and the order of allocation is >= sbi->s_mb_order2_reqs. The * value of s_mb_order2_reqs can be tuned via - * /proc/fs/ext4//order2_req. If the request len is equal to + * /sys/fs/ext4//mb_order2_req. If the request len is equal to * stripe size (sbi->s_stripe), we try to search for contigous block in - * stripe size. This should result in better allocation on RAID setup. If - * not we search in the specific group using bitmap for best extents. The - * tunable min_to_scan and max_to_scan controll the behaviour here. + * stripe size. This should result in better allocation on RAID setups. If + * not, we search in the specific group using bitmap for best extents. The + * tunable min_to_scan and max_to_scan control the behaviour here. * min_to_scan indicate how long the mballoc __must__ look for a best - * extent and max_to_scanindicate how long the mballoc __can__ look for a + * extent and max_to_scan indicates how long the mballoc __can__ look for a * best extent in the found extents. Searching for the blocks starts with * the group specified as the goal value in allocation context via * ac_g_ex. Each group is first checked based on the criteria whether it @@ -337,8 +338,6 @@ static void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap, ext4_group_t group); static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap, ext4_group_t group); -static int ext4_mb_init_per_dev_proc(struct super_block *sb); -static int ext4_mb_destroy_per_dev_proc(struct super_block *sb); static void release_blocks_on_commit(journal_t *journal, transaction_t *txn); @@ -1978,7 +1977,7 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac) /* * We search using buddy data only if the order of the request * is greater than equal to the sbi_s_mb_order2_reqs - * You can tune it via /proc/fs/ext4//order2_req + * You can tune it via /sys/fs/ext4//mb_order2_req */ if (i >= sbi->s_mb_order2_reqs) { /* @@ -2753,7 +2752,6 @@ int ext4_mb_init(struct super_block *sb, int needs_recovery) spin_lock_init(&lg->lg_prealloc_lock); } - ext4_mb_init_per_dev_proc(sb); ext4_mb_history_init(sb); if (sbi->s_journal) @@ -2836,7 +2834,6 @@ int ext4_mb_release(struct super_block *sb) free_percpu(sbi->s_locality_groups); ext4_mb_history_release(sb); - ext4_mb_destroy_per_dev_proc(sb); return 0; } @@ -2897,62 +2894,6 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn) mb_debug("freed %u blocks in %u structures\n", count, count2); } -#define EXT4_MB_STATS_NAME "stats" -#define EXT4_MB_MAX_TO_SCAN_NAME "max_to_scan" -#define EXT4_MB_MIN_TO_SCAN_NAME "min_to_scan" -#define EXT4_MB_ORDER2_REQ "order2_req" -#define EXT4_MB_STREAM_REQ "stream_req" -#define EXT4_MB_GROUP_PREALLOC "group_prealloc" - -static int ext4_mb_init_per_dev_proc(struct super_block *sb) -{ -#ifdef CONFIG_PROC_FS - mode_t mode = S_IFREG | S_IRUGO | S_IWUSR; - struct ext4_sb_info *sbi = EXT4_SB(sb); - struct proc_dir_entry *proc; - - if (sbi->s_proc == NULL) - return -EINVAL; - - EXT4_PROC_HANDLER(EXT4_MB_STATS_NAME, mb_stats); - EXT4_PROC_HANDLER(EXT4_MB_MAX_TO_SCAN_NAME, mb_max_to_scan); - EXT4_PROC_HANDLER(EXT4_MB_MIN_TO_SCAN_NAME, mb_min_to_scan); - EXT4_PROC_HANDLER(EXT4_MB_ORDER2_REQ, mb_order2_reqs); - EXT4_PROC_HANDLER(EXT4_MB_STREAM_REQ, mb_stream_request); - EXT4_PROC_HANDLER(EXT4_MB_GROUP_PREALLOC, mb_group_prealloc); - return 0; - -err_out: - remove_proc_entry(EXT4_MB_GROUP_PREALLOC, sbi->s_proc); - remove_proc_entry(EXT4_MB_STREAM_REQ, sbi->s_proc); - remove_proc_entry(EXT4_MB_ORDER2_REQ, sbi->s_proc); - remove_proc_entry(EXT4_MB_MIN_TO_SCAN_NAME, sbi->s_proc); - remove_proc_entry(EXT4_MB_MAX_TO_SCAN_NAME, sbi->s_proc); - remove_proc_entry(EXT4_MB_STATS_NAME, sbi->s_proc); - return -ENOMEM; -#else - return 0; -#endif -} - -static int ext4_mb_destroy_per_dev_proc(struct super_block *sb) -{ -#ifdef CONFIG_PROC_FS - struct ext4_sb_info *sbi = EXT4_SB(sb); - - if (sbi->s_proc == NULL) - return -EINVAL; - - remove_proc_entry(EXT4_MB_GROUP_PREALLOC, sbi->s_proc); - remove_proc_entry(EXT4_MB_STREAM_REQ, sbi->s_proc); - remove_proc_entry(EXT4_MB_ORDER2_REQ, sbi->s_proc); - remove_proc_entry(EXT4_MB_MIN_TO_SCAN_NAME, sbi->s_proc); - remove_proc_entry(EXT4_MB_MAX_TO_SCAN_NAME, sbi->s_proc); - remove_proc_entry(EXT4_MB_STATS_NAME, sbi->s_proc); -#endif - return 0; -} - int __init init_ext4_mballoc(void) { ext4_pspace_cachep = @@ -3123,7 +3064,7 @@ out_err: * here we normalize request for locality group * Group request are normalized to s_strip size if we set the same via mount * option. If not we set it to s_mb_group_prealloc which can be configured via - * /proc/fs/ext4//group_prealloc + * /sys/fs/ext4//mb_group_prealloc * * XXX: should we try to preallocate more than the group has now? */ @@ -4239,7 +4180,7 @@ static inline void ext4_mb_show_ac(struct ext4_allocation_context *ac) * file is determined by the current size or the resulting size after * allocation which ever is larger * - * One can tune this size via /proc/fs/ext4//stream_req + * One can tune this size via /sys/fs/ext4//mb_stream_req */ static void ext4_mb_group_or_file(struct ext4_allocation_context *ac) { diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 2883d4318c22..1ec554cc107a 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -579,7 +579,6 @@ static void ext4_put_super(struct super_block *sb) ext4_commit_super(sb, es, 1); } if (sbi->s_proc) { - remove_proc_entry("inode_readahead_blks", sbi->s_proc); remove_proc_entry(sb->s_id, ext4_proc_root); } kobject_del(&sbi->s_kobj); @@ -2529,11 +2528,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) #ifdef CONFIG_PROC_FS if (ext4_proc_root) sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root); - - if (sbi->s_proc) - proc_create_data("inode_readahead_blks", 0644, sbi->s_proc, - &ext4_ui_proc_fops, - &sbi->s_inode_readahead_blks); #endif bgl_lock_init(sbi->s_blockgroup_lock); @@ -2832,7 +2826,6 @@ failed_mount2: kfree(sbi->s_group_desc); failed_mount: if (sbi->s_proc) { - remove_proc_entry("inode_readahead_blks", sbi->s_proc); remove_proc_entry(sb->s_id, ext4_proc_root); } #ifdef CONFIG_QUOTA @@ -3865,45 +3858,6 @@ static int ext4_get_sb(struct file_system_type *fs_type, return get_sb_bdev(fs_type, flags, dev_name, data, ext4_fill_super, mnt); } -#ifdef CONFIG_PROC_FS -static int ext4_ui_proc_show(struct seq_file *m, void *v) -{ - unsigned int *p = m->private; - - seq_printf(m, "%u\n", *p); - return 0; -} - -static int ext4_ui_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, ext4_ui_proc_show, PDE(inode)->data); -} - -static ssize_t ext4_ui_proc_write(struct file *file, const char __user *buf, - size_t cnt, loff_t *ppos) -{ - unsigned long *p = PDE(file->f_path.dentry->d_inode)->data; - char str[32]; - - if (cnt >= sizeof(str)) - return -EINVAL; - if (copy_from_user(str, buf, cnt)) - return -EFAULT; - - *p = simple_strtoul(str, NULL, 0); - return cnt; -} - -const struct file_operations ext4_ui_proc_fops = { - .owner = THIS_MODULE, - .open = ext4_ui_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = ext4_ui_proc_write, -}; -#endif - static struct file_system_type ext4_fs_type = { .owner = THIS_MODULE, .name = "ext4", -- cgit v1.2.3-59-g8ed1b From a1aade478862fca8710904532cbc6efed97fd05a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 4 Mar 2009 16:11:35 -0800 Subject: x86/doc: mini-howto for using earlyprintk=dbgp [ mingo: small edits and extensions. ] Signed-off-by: Yinghai Lu Cc: "Eric W. Biederman" Cc: Greg KH Cc: Randy Dunlap Cc: Alan Stern Cc: Sarah Sharp Cc: Andrew Morton LKML-Reference: <49AF18B7.4050305@kernel.org> Signed-off-by: Ingo Molnar --- Documentation/x86/earlyprintk.txt | 101 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Documentation/x86/earlyprintk.txt (limited to 'Documentation') diff --git a/Documentation/x86/earlyprintk.txt b/Documentation/x86/earlyprintk.txt new file mode 100644 index 000000000000..607b1a016064 --- /dev/null +++ b/Documentation/x86/earlyprintk.txt @@ -0,0 +1,101 @@ + +Mini-HOWTO for using the earlyprintk=dbgp boot option with a +USB2 Debug port key and a debug cable, on x86 systems. + +You need two computers, the 'USB debug key' special gadget and +and two USB cables, connected like this: + + [host/target] <-------> [USB debug key] <-------> [client/console] + +1. There are three specific hardware requirements: + + a.) Host/target system needs to have USB debug port capability. + + You can check this capability by looking at a 'Debug port' bit in + the lspci -vvv output: + + # lspci -vvv + ... + 00:1d.7 USB Controller: Intel Corporation 82801H (ICH8 Family) USB2 EHCI Controller #1 (rev 03) (prog-if 20 [EHCI]) + Subsystem: Lenovo ThinkPad T61 + Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx- + Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- SERR- /proc/sysrq-trigger + + On the host/target system you should see this help line in "dmesg" output: + + SysRq : HELP : loglevel(0-9) reBoot Crashdump terminate-all-tasks(E) memory-full-oom-kill(F) kill-all-tasks(I) saK show-backtrace-all-active-cpus(L) show-memory-usage(M) nice-all-RT-tasks(N) powerOff show-registers(P) show-all-timers(Q) unRaw Sync show-task-states(T) Unmount show-blocked-tasks(W) dump-ftrace-buffer(Z) + + On the client/console system do: + + cat /dev/ttyUSB0 + + And you should see the help line above displayed shortly after you've + provoked it on the host system. + +If it does not work then please ask about it on the linux-kernel@vger.kernel.org +mailing list or contact the x86 maintainers. -- cgit v1.2.3-59-g8ed1b From 59d3daafa17265f01149df8eab3fb69b9b42cb2e Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 9 Jan 2009 13:01:56 +0100 Subject: dma-debug: add kernel command line parameters Impact: add dma_debug= and dma_debug_entries= kernel parameters Signed-off-by: Joerg Roedel --- Documentation/kernel-parameters.txt | 10 ++++++++++ lib/dma-debug.c | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 54f21a5c262b..0fa3c0545994 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -491,6 +491,16 @@ and is between 256 and 4096 characters. It is defined in the file Range: 0 - 8192 Default: 64 + dma_debug=off If the kernel is compiled with DMA_API_DEBUG support + this option disables the debugging code at boot. + + dma_debug_entries= + This option allows to tune the number of preallocated + entries for DMA-API debugging code. One entry is + required per DMA-API allocation. Use this if the + DMA-API debugging code disables itself because the + architectural default is too low. + hpet= [X86-32,HPET] option to control HPET usage Format: { enable (default) | disable | force } disable: disable HPET and use PIT instead diff --git a/lib/dma-debug.c b/lib/dma-debug.c index 5b50bb31f7c6..2ede46308024 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c @@ -64,6 +64,9 @@ static bool global_disable __read_mostly; static u32 num_free_entries; static u32 min_free_entries; +/* number of preallocated entries requested by kernel cmdline */ +static u32 req_entries; + /* * Hash related functions * @@ -253,6 +256,9 @@ void dma_debug_init(u32 num_entries) dma_entry_hash[i].lock = SPIN_LOCK_UNLOCKED; } + if (req_entries) + num_entries = req_entries; + if (prealloc_memory(num_entries) != 0) { printk(KERN_ERR "DMA-API: debugging out of memory error " "- disabled\n"); @@ -264,3 +270,35 @@ void dma_debug_init(u32 num_entries) printk(KERN_INFO "DMA-API: debugging enabled by kernel config\n"); } +static __init int dma_debug_cmdline(char *str) +{ + if (!str) + return -EINVAL; + + if (strncmp(str, "off", 3) == 0) { + printk(KERN_INFO "DMA-API: debugging disabled on kernel " + "command line\n"); + global_disable = true; + } + + return 0; +} + +static __init int dma_debug_entries_cmdline(char *str) +{ + int res; + + if (!str) + return -EINVAL; + + res = get_option(&str, &req_entries); + + if (!res) + req_entries = 0; + + return 0; +} + +__setup("dma_debug=", dma_debug_cmdline); +__setup("dma_debug_entries=", dma_debug_entries_cmdline); + -- cgit v1.2.3-59-g8ed1b From 4e59c25dcbc1f033d043f1009a7f6aaa1f2aef26 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 12 Dec 2008 00:24:18 +0000 Subject: [ARM] S3C: Rename s3c2410_pm_init to s3c_pm_init. Since we have moved a large proportion of the PM code to the common support area, remove the cpu specific name from the initialisation function. Signed-off-by: Ben Dooks --- Documentation/arm/Samsung-S3C24XX/Suspend.txt | 8 ++++---- arch/arm/mach-s3c2410/mach-h1940.c | 2 +- arch/arm/mach-s3c2410/mach-qt2410.c | 2 +- arch/arm/mach-s3c2412/mach-jive.c | 2 +- arch/arm/mach-s3c2440/mach-rx3715.c | 2 +- arch/arm/plat-s3c/include/plat/pm.h | 6 +++--- arch/arm/plat-s3c/pm.c | 4 ++-- arch/arm/plat-s3c24xx/common-smdk.c | 2 +- arch/arm/plat-s3c24xx/pm-simtec.c | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) (limited to 'Documentation') diff --git a/Documentation/arm/Samsung-S3C24XX/Suspend.txt b/Documentation/arm/Samsung-S3C24XX/Suspend.txt index 0dab6e32c130..a30fe510572b 100644 --- a/Documentation/arm/Samsung-S3C24XX/Suspend.txt +++ b/Documentation/arm/Samsung-S3C24XX/Suspend.txt @@ -40,13 +40,13 @@ Resuming Machine Support --------------- - The machine specific functions must call the s3c2410_pm_init() function + The machine specific functions must call the s3c_pm_init() function to say that its bootloader is capable of resuming. This can be as simple as adding the following to the machine's definition: - INITMACHINE(s3c2410_pm_init) + INITMACHINE(s3c_pm_init) - A board can do its own setup before calling s3c2410_pm_init, if it + A board can do its own setup before calling s3c_pm_init, if it needs to setup anything else for power management support. There is currently no support for over-riding the default method of @@ -74,7 +74,7 @@ statuc void __init machine_init(void) enable_irq_wake(IRQ_EINT0); - s3c2410_pm_init(); + s3c_pm_init(); } diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 821a1668c3ac..7a7c4da4c256 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -203,7 +203,7 @@ static void __init h1940_map_io(void) #ifdef CONFIG_PM_H1940 memcpy(phys_to_virt(H1940_SUSPEND_RESUMEAT), h1940_pm_return, 1024); #endif - s3c2410_pm_init(); + s3c_pm_init(); } static void __init h1940_init_irq(void) diff --git a/arch/arm/mach-s3c2410/mach-qt2410.c b/arch/arm/mach-s3c2410/mach-qt2410.c index 9678a53ceeb1..9f1ba9b63f70 100644 --- a/arch/arm/mach-s3c2410/mach-qt2410.c +++ b/arch/arm/mach-s3c2410/mach-qt2410.c @@ -355,7 +355,7 @@ static void __init qt2410_machine_init(void) s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT); platform_add_devices(qt2410_devices, ARRAY_SIZE(qt2410_devices)); - s3c2410_pm_init(); + s3c_pm_init(); } MACHINE_START(QT2410, "QT2410") diff --git a/arch/arm/mach-s3c2412/mach-jive.c b/arch/arm/mach-s3c2412/mach-jive.c index ecddbbb34832..50d8054292c2 100644 --- a/arch/arm/mach-s3c2412/mach-jive.c +++ b/arch/arm/mach-s3c2412/mach-jive.c @@ -630,7 +630,7 @@ static void __init jive_machine_init(void) /* initialise the power management now we've setup everything. */ - s3c2410_pm_init(); + s3c_pm_init(); s3c_device_nand.dev.platform_data = &jive_nand_info; diff --git a/arch/arm/mach-s3c2440/mach-rx3715.c b/arch/arm/mach-s3c2440/mach-rx3715.c index 12d378f84ad2..bc8d8d1ebd1a 100644 --- a/arch/arm/mach-s3c2440/mach-rx3715.c +++ b/arch/arm/mach-s3c2440/mach-rx3715.c @@ -203,7 +203,7 @@ static void __init rx3715_init_machine(void) #ifdef CONFIG_PM_H1940 memcpy(phys_to_virt(H1940_SUSPEND_RESUMEAT), h1940_pm_return, 1024); #endif - s3c2410_pm_init(); + s3c_pm_init(); s3c24xx_fb_set_platdata(&rx3715_fb_info); platform_add_devices(rx3715_devices, ARRAY_SIZE(rx3715_devices)); diff --git a/arch/arm/plat-s3c/include/plat/pm.h b/arch/arm/plat-s3c/include/plat/pm.h index 5e27de955da0..a6104c8055ff 100644 --- a/arch/arm/plat-s3c/include/plat/pm.h +++ b/arch/arm/plat-s3c/include/plat/pm.h @@ -9,7 +9,7 @@ * published by the Free Software Foundation. */ -/* s3c2410_pm_init +/* s3c_pm_init * * called from board at initialisation time to setup the power * management @@ -17,11 +17,11 @@ #ifdef CONFIG_PM -extern __init int s3c2410_pm_init(void); +extern __init int s3c_pm_init(void); #else -static inline int s3c2410_pm_init(void) +static inline int s3c_pm_init(void) { return 0; } diff --git a/arch/arm/plat-s3c/pm.c b/arch/arm/plat-s3c/pm.c index 7c736deff8ae..e82ec628ced1 100644 --- a/arch/arm/plat-s3c/pm.c +++ b/arch/arm/plat-s3c/pm.c @@ -316,14 +316,14 @@ static struct platform_suspend_ops s3c_pm_ops = { .valid = suspend_valid_only_mem, }; -/* s3c2410_pm_init +/* s3c_pm_init * * Attach the power management functions. This should be called * from the board specific initialisation if the board supports * it. */ -int __init s3c2410_pm_init(void) +int __init s3c_pm_init(void) { printk("S3C Power Management, Copyright 2004 Simtec Electronics\n"); diff --git a/arch/arm/plat-s3c24xx/common-smdk.c b/arch/arm/plat-s3c24xx/common-smdk.c index 3d4837021ac7..1a8347cec20a 100644 --- a/arch/arm/plat-s3c24xx/common-smdk.c +++ b/arch/arm/plat-s3c24xx/common-smdk.c @@ -201,5 +201,5 @@ void __init smdk_machine_init(void) platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs)); - s3c2410_pm_init(); + s3c_pm_init(); } diff --git a/arch/arm/plat-s3c24xx/pm-simtec.c b/arch/arm/plat-s3c24xx/pm-simtec.c index 21dfa74773d1..da0d3217d3e3 100644 --- a/arch/arm/plat-s3c24xx/pm-simtec.c +++ b/arch/arm/plat-s3c24xx/pm-simtec.c @@ -61,7 +61,7 @@ static __init int pm_simtec_init(void) __raw_writel(gstatus4, S3C2410_GSTATUS4); - return s3c2410_pm_init(); + return s3c_pm_init(); } arch_initcall(pm_simtec_init); -- cgit v1.2.3-59-g8ed1b From 79c7cdd5441f5d3900c1632adcc8cd2bee35c8da Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 9 Feb 2009 14:47:19 +0100 Subject: ALSA: Add kernel-doc comments to vmaster stuff Signed-off-by: Takashi Iwai --- .../sound/alsa/DocBook/alsa-driver-api.tmpl | 4 +++ include/sound/control.h | 32 ++++++++++++++++++++++ sound/core/vmaster.c | 16 +++++++++-- 3 files changed, 50 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl index 9d644f7e241e..115962827c81 100644 --- a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl +++ b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl @@ -71,6 +71,10 @@ !Esound/pci/ac97/ac97_codec.c !Esound/pci/ac97/ac97_pcm.c + Virtual Master Control API +!Esound/core/vmaster.c +!Iinclude/sound/control.h + MIDI API Raw MIDI API diff --git a/include/sound/control.h b/include/sound/control.h index 4cf8f7aaa13f..ef96f07aa03b 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -176,12 +176,44 @@ int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave, /* optional flags for slave */ #define SND_CTL_SLAVE_NEED_UPDATE (1 << 0) +/** + * snd_ctl_add_slave - Add a virtual slave control + * @master: vmaster element + * @slave: slave element to add + * + * Add a virtual slave control to the given master element created via + * snd_ctl_create_virtual_master() beforehand. + * Returns zero if successful or a negative error code. + * + * All slaves must be the same type (returning the same information + * via info callback). The fucntion doesn't check it, so it's your + * responsibility. + * + * Also, some additional limitations: + * at most two channels, + * logarithmic volume control (dB level) thus no linear volume, + * master can only attenuate the volume without gain + */ static inline int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) { return _snd_ctl_add_slave(master, slave, 0); } +/** + * snd_ctl_add_slave_uncached - Add a virtual slave control + * @master: vmaster element + * @slave: slave element to add + * + * Add a virtual slave control to the given master. + * Unlike snd_ctl_add_slave(), the element added via this function + * is supposed to have volatile values, and get callback is called + * at each time quried from the master. + * + * When the control peeks the hardware values directly and the value + * can be changed by other means than the put callback of the element, + * this function should be used to keep the value always up-to-date. + */ static inline int snd_ctl_add_slave_uncached(struct snd_kcontrol *master, struct snd_kcontrol *slave) diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index d51b198d06d9..257624bd1997 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -340,8 +340,20 @@ static void master_free(struct snd_kcontrol *kcontrol) } -/* - * Create a virtual master control with the given name +/** + * snd_ctl_make_virtual_master - Create a virtual master control + * @name: name string of the control element to create + * @tlv: optional TLV int array for dB information + * + * Creates a virtual matster control with the given name string. + * Returns the created control element, or NULL for errors (ENOMEM). + * + * After creating a vmaster element, you can add the slave controls + * via snd_ctl_add_slave() or snd_ctl_add_slave_uncached(). + * + * The optional argument @tlv can be used to specify the TLV information + * for dB scale of the master control. It should be a single element + * with #SNDRV_CTL_TLVT_DB_SCALE type, and should be the max 0dB. */ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, const unsigned int *tlv) -- cgit v1.2.3-59-g8ed1b From 662c319ae4b4fb60001816dfe1dde5fdfc7a2af9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 9 Feb 2009 08:53:50 +0100 Subject: ALSA: Add sound/core/jack.c to driver-API docbook entry Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl index 9d644f7e241e..37b006cdf2f9 100644 --- a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl +++ b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl @@ -88,6 +88,9 @@ Miscellaneous Functions Hardware-Dependent Devices API !Esound/core/hwdep.c + + Jack Abstraction Layer API +!Esound/core/jack.c ISA DMA Helpers !Esound/core/isadma.c -- cgit v1.2.3-59-g8ed1b From 5f8206c04857965cc2ff6c395633c4fdd977dd77 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 9 Feb 2009 08:50:43 +0100 Subject: ALSA: Fix DocBook headers Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl | 10 ++++++---- Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl index 90f163c4bde9..0230a96f0564 100644 --- a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl +++ b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl @@ -1,11 +1,11 @@ - - - - + + + The ALSA Driver API @@ -35,6 +35,8 @@ + + Management of Cards and Devices Card Management !Esound/core/init.c diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index 320384c1791b..46b08fef3744 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -1,11 +1,11 @@ - - - - + + + Writing an ALSA Driver -- cgit v1.2.3-59-g8ed1b From e776ec19a47a325ee1d9ece2d983526dcd626c53 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 28 Feb 2009 17:40:18 +0100 Subject: ALSA: Move ALSA docbooks to be with the rest of the kernel docbooks Move ALSA docbooks to be with the rest of the kernel docbooks and add them to the Makefile so that they build. Latter required a few minor changes to alsa .tmpl files. (I did not remove all of the trailing whitespace in the .tmpl files.) Fixes kernel bugzilla #12726: http://bugzilla.kernel.org/show_bug.cgi?id=12726 Signed-off-by: Randy Dunlap Cc: documentation_man-pages@kernel-bugs.osdl.org Cc: Nicola Soranzo Signed-off-by: Takashi Iwai --- Documentation/DocBook/Makefile | 3 +- Documentation/DocBook/alsa-driver-api.tmpl | 109 + Documentation/DocBook/writing-an-alsa-driver.tmpl | 6216 ++++++++++++++++++++ .../sound/alsa/DocBook/alsa-driver-api.tmpl | 109 - .../sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 6216 -------------------- 5 files changed, 6327 insertions(+), 6326 deletions(-) create mode 100644 Documentation/DocBook/alsa-driver-api.tmpl create mode 100644 Documentation/DocBook/writing-an-alsa-driver.tmpl delete mode 100644 Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl delete mode 100644 Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl (limited to 'Documentation') diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 1462ed86d40a..a3a83d38f96f 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -12,7 +12,8 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \ kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \ gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \ genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ - mac80211.xml debugobjects.xml sh.xml regulator.xml + mac80211.xml debugobjects.xml sh.xml regulator.xml \ + alsa-driver-api.xml writing-an-alsa-driver.xml ### # The build process is as follows (targets): diff --git a/Documentation/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl new file mode 100644 index 000000000000..0230a96f0564 --- /dev/null +++ b/Documentation/DocBook/alsa-driver-api.tmpl @@ -0,0 +1,109 @@ + + + + + + + + + The ALSA Driver API + + + + This document is free; 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 document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + + + + + Management of Cards and Devices + Card Management +!Esound/core/init.c + + Device Components +!Esound/core/device.c + + Module requests and Device File Entries +!Esound/core/sound.c + + Memory Management Helpers +!Esound/core/memory.c +!Esound/core/memalloc.c + + + PCM API + PCM Core +!Esound/core/pcm.c +!Esound/core/pcm_lib.c +!Esound/core/pcm_native.c + + PCM Format Helpers +!Esound/core/pcm_misc.c + + PCM Memory Management +!Esound/core/pcm_memory.c + + + Control/Mixer API + General Control Interface +!Esound/core/control.c + + AC97 Codec API +!Esound/pci/ac97/ac97_codec.c +!Esound/pci/ac97/ac97_pcm.c + + Virtual Master Control API +!Esound/core/vmaster.c +!Iinclude/sound/control.h + + + MIDI API + Raw MIDI API +!Esound/core/rawmidi.c + + MPU401-UART API +!Esound/drivers/mpu401/mpu401_uart.c + + + Proc Info API + Proc Info Interface +!Esound/core/info.c + + + Miscellaneous Functions + Hardware-Dependent Devices API +!Esound/core/hwdep.c + + Jack Abstraction Layer API +!Esound/core/jack.c + + ISA DMA Helpers +!Esound/core/isadma.c + + Other Helper Macros +!Iinclude/sound/core.h + + + + diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl new file mode 100644 index 000000000000..46b08fef3744 --- /dev/null +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -0,0 +1,6216 @@ + + + + + + + + + Writing an ALSA Driver + + Takashi + Iwai + +
+ tiwai@suse.de +
+
+
+ + Oct 15, 2007 + 0.3.7 + + + + This document describes how to write an ALSA (Advanced Linux + Sound Architecture) driver. + + + + + + Copyright (c) 2002-2005 Takashi Iwai tiwai@suse.de + + + + This document is free; 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 document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + +
+ + + + + + Preface + + This document describes how to write an + + ALSA (Advanced Linux Sound Architecture) + driver. The document focuses mainly on PCI soundcards. + In the case of other device types, the API might + be different, too. However, at least the ALSA kernel API is + consistent, and therefore it would be still a bit help for + writing them. + + + + This document targets people who already have enough + C language skills and have basic linux kernel programming + knowledge. This document doesn't explain the general + topic of linux kernel coding and doesn't cover low-level + driver implementation details. It only describes + the standard way to write a PCI sound driver on ALSA. + + + + If you are already familiar with the older ALSA ver.0.5.x API, you + can check the drivers such as sound/pci/es1938.c or + sound/pci/maestro3.c which have also almost the same + code-base in the ALSA 0.5.x tree, so you can compare the differences. + + + + This document is still a draft version. Any feedback and + corrections, please!! + + + + + + + + + File Tree Structure + +
+ General + + The ALSA drivers are provided in two ways. + + + + One is the trees provided as a tarball or via cvs from the + ALSA's ftp site, and another is the 2.6 (or later) Linux kernel + tree. To synchronize both, the ALSA driver tree is split into + two different trees: alsa-kernel and alsa-driver. The former + contains purely the source code for the Linux 2.6 (or later) + tree. This tree is designed only for compilation on 2.6 or + later environment. The latter, alsa-driver, contains many subtle + files for compiling ALSA drivers outside of the Linux kernel tree, + wrapper functions for older 2.2 and 2.4 kernels, to adapt the latest kernel API, + and additional drivers which are still in development or in + tests. The drivers in alsa-driver tree will be moved to + alsa-kernel (and eventually to the 2.6 kernel tree) when they are + finished and confirmed to work fine. + + + + The file tree structure of ALSA driver is depicted below. Both + alsa-kernel and alsa-driver have almost the same file + structure, except for core directory. It's + named as acore in alsa-driver tree. + + + ALSA File Tree Structure + + sound + /core + /oss + /seq + /oss + /instr + /ioctl32 + /include + /drivers + /mpu401 + /opl3 + /i2c + /l3 + /synth + /emux + /pci + /(cards) + /isa + /(cards) + /arm + /ppc + /sparc + /usb + /pcmcia /(cards) + /oss + + + +
+ +
+ core directory + + This directory contains the middle layer which is the heart + of ALSA drivers. In this directory, the native ALSA modules are + stored. The sub-directories contain different modules and are + dependent upon the kernel config. + + +
+ core/oss + + + The codes for PCM and mixer OSS emulation modules are stored + in this directory. The rawmidi OSS emulation is included in + the ALSA rawmidi code since it's quite small. The sequencer + code is stored in core/seq/oss directory (see + + below). + +
+ +
+ core/ioctl32 + + + This directory contains the 32bit-ioctl wrappers for 64bit + architectures such like x86-64, ppc64 and sparc64. For 32bit + and alpha architectures, these are not compiled. + +
+ +
+ core/seq + + This directory and its sub-directories are for the ALSA + sequencer. This directory contains the sequencer core and + primary sequencer modules such like snd-seq-midi, + snd-seq-virmidi, etc. They are compiled only when + CONFIG_SND_SEQUENCER is set in the kernel + config. + +
+ +
+ core/seq/oss + + This contains the OSS sequencer emulation codes. + +
+ +
+ core/seq/instr + + This directory contains the modules for the sequencer + instrument layer. + +
+
+ +
+ include directory + + This is the place for the public header files of ALSA drivers, + which are to be exported to user-space, or included by + several files at different directories. Basically, the private + header files should not be placed in this directory, but you may + still find files there, due to historical reasons :) + +
+ +
+ drivers directory + + This directory contains code shared among different drivers + on different architectures. They are hence supposed not to be + architecture-specific. + For example, the dummy pcm driver and the serial MIDI + driver are found in this directory. In the sub-directories, + there is code for components which are independent from + bus and cpu architectures. + + +
+ drivers/mpu401 + + The MPU401 and MPU401-UART modules are stored here. + +
+ +
+ drivers/opl3 and opl4 + + The OPL3 and OPL4 FM-synth stuff is found here. + +
+
+ +
+ i2c directory + + This contains the ALSA i2c components. + + + + Although there is a standard i2c layer on Linux, ALSA has its + own i2c code for some cards, because the soundcard needs only a + simple operation and the standard i2c API is too complicated for + such a purpose. + + +
+ i2c/l3 + + This is a sub-directory for ARM L3 i2c. + +
+
+ +
+ synth directory + + This contains the synth middle-level modules. + + + + So far, there is only Emu8000/Emu10k1 synth driver under + the synth/emux sub-directory. + +
+ +
+ pci directory + + This directory and its sub-directories hold the top-level card modules + for PCI soundcards and the code specific to the PCI BUS. + + + + The drivers compiled from a single file are stored directly + in the pci directory, while the drivers with several source files are + stored on their own sub-directory (e.g. emu10k1, ice1712). + +
+ +
+ isa directory + + This directory and its sub-directories hold the top-level card modules + for ISA soundcards. + +
+ +
+ arm, ppc, and sparc directories + + They are used for top-level card modules which are + specific to one of these architectures. + +
+ +
+ usb directory + + This directory contains the USB-audio driver. In the latest version, the + USB MIDI driver is integrated in the usb-audio driver. + +
+ +
+ pcmcia directory + + The PCMCIA, especially PCCard drivers will go here. CardBus + drivers will be in the pci directory, because their API is identical + to that of standard PCI cards. + +
+ +
+ oss directory + + The OSS/Lite source files are stored here in Linux 2.6 (or + later) tree. In the ALSA driver tarball, this directory is empty, + of course :) + +
+
+ + + + + + + Basic Flow for PCI Drivers + +
+ Outline + + The minimum flow for PCI soundcards is as follows: + + + define the PCI ID table (see the section + PCI Entries + ). + create probe() callback. + create remove() callback. + create a pci_driver structure + containing the three pointers above. + create an init() function just calling + the pci_register_driver() to register the pci_driver table + defined above. + create an exit() function to call + the pci_unregister_driver() function. + + +
+ +
+ Full Code Example + + The code example is shown below. Some parts are kept + unimplemented at this moment but will be filled in the + next sections. The numbers in the comment lines of the + snd_mychip_probe() function + refer to details explained in the following section. + + + Basic Flow for PCI Drivers - Example + + + #include + #include + #include + #include + + /* module parameters (see "Module Parameters") */ + /* SNDRV_CARDS: maximum number of cards supported by this module */ + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; + static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + + /* definition of the chip-specific record */ + struct mychip { + struct snd_card *card; + /* the rest of the implementation will be in section + * "PCI Resource Management" + */ + }; + + /* chip-specific destructor + * (see "PCI Resource Management") + */ + static int snd_mychip_free(struct mychip *chip) + { + .... /* will be implemented later... */ + } + + /* component-destructor + * (see "Management of Cards and Components") + */ + static int snd_mychip_dev_free(struct snd_device *device) + { + return snd_mychip_free(device->device_data); + } + + /* chip-specific constructor + * (see "Management of Cards and Components") + */ + static int __devinit snd_mychip_create(struct snd_card *card, + struct pci_dev *pci, + struct mychip **rchip) + { + struct mychip *chip; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_mychip_dev_free, + }; + + *rchip = NULL; + + /* check PCI availability here + * (see "PCI Resource Management") + */ + .... + + /* allocate a chip-specific data with zero filled */ + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->card = card; + + /* rest of initialization here; will be implemented + * later, see "PCI Resource Management" + */ + .... + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + snd_mychip_free(chip); + return err; + } + + snd_card_set_dev(card, &pci->dev); + + *rchip = chip; + return 0; + } + + /* constructor -- see "Constructor" sub-section */ + static int __devinit snd_mychip_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) + { + static int dev; + struct snd_card *card; + struct mychip *chip; + int err; + + /* (1) */ + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + /* (2) */ + err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); + if (err < 0) + return err; + + /* (3) */ + err = snd_mychip_create(card, pci, &chip); + if (err < 0) { + snd_card_free(card); + return err; + } + + /* (4) */ + strcpy(card->driver, "My Chip"); + strcpy(card->shortname, "My Own Chip 123"); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->ioport, chip->irq); + + /* (5) */ + .... /* implemented later */ + + /* (6) */ + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + + /* (7) */ + pci_set_drvdata(pci, card); + dev++; + return 0; + } + + /* destructor -- see the "Destructor" sub-section */ + static void __devexit snd_mychip_remove(struct pci_dev *pci) + { + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); + } +]]> + + + +
+ +
+ Constructor + + The real constructor of PCI drivers is the probe callback. + The probe callback and other component-constructors which are called + from the probe callback should be defined with + the __devinit prefix. You + cannot use the __init prefix for them, + because any PCI device could be a hotplug device. + + + + In the probe callback, the following scheme is often used. + + +
+ 1) Check and increment the device index. + + + += SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } +]]> + + + + where enable[dev] is the module option. + + + + Each time the probe callback is called, check the + availability of the device. If not available, simply increment + the device index and returns. dev will be incremented also + later (step + 7). + +
+ +
+ 2) Create a card instance + + + + + + + + + + The details will be explained in the section + + Management of Cards and Components. + +
+ +
+ 3) Create a main component + + In this part, the PCI resources are allocated. + + + + + + + + The details will be explained in the section PCI Resource + Management. + +
+ +
+ 4) Set the driver ID and name strings. + + + +driver, "My Chip"); + strcpy(card->shortname, "My Own Chip 123"); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->ioport, chip->irq); +]]> + + + + The driver field holds the minimal ID string of the + chip. This is used by alsa-lib's configurator, so keep it + simple but unique. + Even the same driver can have different driver IDs to + distinguish the functionality of each chip type. + + + + The shortname field is a string shown as more verbose + name. The longname field contains the information + shown in /proc/asound/cards. + +
+ +
+ 5) Create other components, such as mixer, MIDI, etc. + + Here you define the basic components such as + PCM, + mixer (e.g. AC97), + MIDI (e.g. MPU-401), + and other interfaces. + Also, if you want a proc + file, define it here, too. + +
+ +
+ 6) Register the card instance. + + + + + + + + + + Will be explained in the section Management + of Cards and Components, too. + +
+ +
+ 7) Set the PCI driver data and return zero. + + + + + + + + In the above, the card record is stored. This pointer is + used in the remove callback and power-management + callbacks, too. + +
+
+ +
+ Destructor + + The destructor, remove callback, simply releases the card + instance. Then the ALSA middle layer will release all the + attached components automatically. + + + + It would be typically like the following: + + + + + + + + The above code assumes that the card pointer is set to the PCI + driver data. + +
+ +
+ Header Files + + For the above example, at least the following include files + are necessary. + + + + + #include + #include + #include + #include +]]> + + + + where the last one is necessary only when module options are + defined in the source file. If the code is split into several + files, the files without module options don't need them. + + + + In addition to these headers, you'll need + <linux/interrupt.h> for interrupt + handling, and <asm/io.h> for I/O + access. If you use the mdelay() or + udelay() functions, you'll need to include + <linux/delay.h> too. + + + + The ALSA interfaces like the PCM and control APIs are defined in other + <sound/xxx.h> header files. + They have to be included after + <sound/core.h>. + + +
+
+ + + + + + + Management of Cards and Components + +
+ Card Instance + + For each soundcard, a card record must be allocated. + + + + A card record is the headquarters of the soundcard. It manages + the whole list of devices (components) on the soundcard, such as + PCM, mixers, MIDI, synthesizer, and so on. Also, the card + record holds the ID and the name strings of the card, manages + the root of proc files, and controls the power-management states + and hotplug disconnections. The component list on the card + record is used to manage the correct release of resources at + destruction. + + + + As mentioned above, to create a card instance, call + snd_card_create(). + + + + + + + + + + The function takes five arguments, the card-index number, the + id string, the module pointer (usually + THIS_MODULE), + the size of extra-data space, and the pointer to return the + card instance. The extra_size argument is used to + allocate card->private_data for the + chip-specific data. Note that these data + are allocated by snd_card_create(). + +
+ +
+ Components + + After the card is created, you can attach the components + (devices) to the card instance. In an ALSA driver, a component is + represented as a struct snd_device object. + A component can be a PCM instance, a control interface, a raw + MIDI interface, etc. Each such instance has one component + entry. + + + + A component can be created via + snd_device_new() function. + + + + + + + + + + This takes the card pointer, the device-level + (SNDRV_DEV_XXX), the data pointer, and the + callback pointers (&ops). The + device-level defines the type of components and the order of + registration and de-registration. For most components, the + device-level is already defined. For a user-defined component, + you can use SNDRV_DEV_LOWLEVEL. + + + + This function itself doesn't allocate the data space. The data + must be allocated manually beforehand, and its pointer is passed + as the argument. This pointer is used as the + (chip identifier in the above example) + for the instance. + + + + Each pre-defined ALSA component such as ac97 and pcm calls + snd_device_new() inside its + constructor. The destructor for each component is defined in the + callback pointers. Hence, you don't need to take care of + calling a destructor for such a component. + + + + If you wish to create your own component, you need to + set the destructor function to the dev_free callback in + the ops, so that it can be released + automatically via snd_card_free(). + The next example will show an implementation of chip-specific + data. + +
+ +
+ Chip-Specific Data + + Chip-specific information, e.g. the I/O port address, its + resource pointer, or the irq number, is stored in the + chip-specific record. + + + + + + + + + + In general, there are two ways of allocating the chip record. + + +
+ 1. Allocating via <function>snd_card_create()</function>. + + As mentioned above, you can pass the extra-data-length + to the 4th argument of snd_card_create(), i.e. + + + + + + + + struct mychip is the type of the chip record. + + + + In return, the allocated record can be accessed as + + + +private_data; +]]> + + + + With this method, you don't have to allocate twice. + The record is released together with the card instance. + +
+ +
+ 2. Allocating an extra device. + + + After allocating a card instance via + snd_card_create() (with + 0 on the 4th arg), call + kzalloc(). + + + + + + + + + + The chip record should have the field to hold the card + pointer at least, + + + + + + + + + + Then, set the card pointer in the returned chip instance. + + + +card = card; +]]> + + + + + + Next, initialize the fields, and register this chip + record as a low-level device with a specified + ops, + + + + + + + + snd_mychip_dev_free() is the + device-destructor function, which will call the real + destructor. + + + + + +device_data); + } +]]> + + + + where snd_mychip_free() is the real destructor. + +
+
+ +
+ Registration and Release + + After all components are assigned, register the card instance + by calling snd_card_register(). Access + to the device files is enabled at this point. That is, before + snd_card_register() is called, the + components are safely inaccessible from external side. If this + call fails, exit the probe function after releasing the card via + snd_card_free(). + + + + For releasing the card instance, you can call simply + snd_card_free(). As mentioned earlier, all + components are released automatically by this call. + + + + As further notes, the destructors (both + snd_mychip_dev_free and + snd_mychip_free) cannot be defined with + the __devexit prefix, because they may be + called from the constructor, too, at the false path. + + + + For a device which allows hotplugging, you can use + snd_card_free_when_closed. This one will + postpone the destruction until all devices are closed. + + +
+ +
+ + + + + + + PCI Resource Management + +
+ Full Code Example + + In this section, we'll complete the chip-specific constructor, + destructor and PCI entries. Example code is shown first, + below. + + + PCI Resource Management Example + +irq >= 0) + free_irq(chip->irq, chip); + /* release the I/O ports & memory */ + pci_release_regions(chip->pci); + /* disable the PCI entry */ + pci_disable_device(chip->pci); + /* release the data */ + kfree(chip); + return 0; + } + + /* chip-specific constructor */ + static int __devinit snd_mychip_create(struct snd_card *card, + struct pci_dev *pci, + struct mychip **rchip) + { + struct mychip *chip; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_mychip_dev_free, + }; + + *rchip = NULL; + + /* initialize the PCI entry */ + err = pci_enable_device(pci); + if (err < 0) + return err; + /* check PCI availability (28bit DMA) */ + if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 || + pci_set_consistent_dma_mask(pci, DMA_28BIT_MASK) < 0) { + printk(KERN_ERR "error to set 28bit mask DMA\n"); + pci_disable_device(pci); + return -ENXIO; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + + /* initialize the stuff */ + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + /* (1) PCI resource allocation */ + err = pci_request_regions(pci, "My Chip"); + if (err < 0) { + kfree(chip); + pci_disable_device(pci); + return err; + } + chip->port = pci_resource_start(pci, 0); + if (request_irq(pci->irq, snd_mychip_interrupt, + IRQF_SHARED, "My Chip", chip)) { + printk(KERN_ERR "cannot grab irq %d\n", pci->irq); + snd_mychip_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; + + /* (2) initialization of the chip hardware */ + .... /* (not implemented in this document) */ + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + snd_mychip_free(chip); + return err; + } + + snd_card_set_dev(card, &pci->dev); + + *rchip = chip; + return 0; + } + + /* PCI IDs */ + static struct pci_device_id snd_mychip_ids[] = { + { PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_BAR, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + .... + { 0, } + }; + MODULE_DEVICE_TABLE(pci, snd_mychip_ids); + + /* pci_driver definition */ + static struct pci_driver driver = { + .name = "My Own Chip", + .id_table = snd_mychip_ids, + .probe = snd_mychip_probe, + .remove = __devexit_p(snd_mychip_remove), + }; + + /* module initialization */ + static int __init alsa_card_mychip_init(void) + { + return pci_register_driver(&driver); + } + + /* module clean up */ + static void __exit alsa_card_mychip_exit(void) + { + pci_unregister_driver(&driver); + } + + module_init(alsa_card_mychip_init) + module_exit(alsa_card_mychip_exit) + + EXPORT_NO_SYMBOLS; /* for old kernels only */ +]]> + + + +
+ +
+ Some Hafta's + + The allocation of PCI resources is done in the + probe() function, and usually an extra + xxx_create() function is written for this + purpose. + + + + In the case of PCI devices, you first have to call + the pci_enable_device() function before + allocating resources. Also, you need to set the proper PCI DMA + mask to limit the accessed I/O range. In some cases, you might + need to call pci_set_master() function, + too. + + + + Suppose the 28bit mask, and the code to be added would be like: + + + + + + + +
+ +
+ Resource Allocation + + The allocation of I/O ports and irqs is done via standard kernel + functions. Unlike ALSA ver.0.5.x., there are no helpers for + that. And these resources must be released in the destructor + function (see below). Also, on ALSA 0.9.x, you don't need to + allocate (pseudo-)DMA for PCI like in ALSA 0.5.x. + + + + Now assume that the PCI device has an I/O port with 8 bytes + and an interrupt. Then struct mychip will have the + following fields: + + + + + + + + + + For an I/O port (and also a memory region), you need to have + the resource pointer for the standard resource management. For + an irq, you have to keep only the irq number (integer). But you + need to initialize this number as -1 before actual allocation, + since irq 0 is valid. The port address and its resource pointer + can be initialized as null by + kzalloc() automatically, so you + don't have to take care of resetting them. + + + + The allocation of an I/O port is done like this: + + + +port = pci_resource_start(pci, 0); +]]> + + + + + + + It will reserve the I/O port region of 8 bytes of the given + PCI device. The returned value, chip->res_port, is allocated + via kmalloc() by + request_region(). The pointer must be + released via kfree(), but there is a + problem with this. This issue will be explained later. + + + + The allocation of an interrupt source is done like this: + + + +irq, snd_mychip_interrupt, + IRQF_SHARED, "My Chip", chip)) { + printk(KERN_ERR "cannot grab irq %d\n", pci->irq); + snd_mychip_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; +]]> + + + + where snd_mychip_interrupt() is the + interrupt handler defined later. + Note that chip->irq should be defined + only when request_irq() succeeded. + + + + On the PCI bus, interrupts can be shared. Thus, + IRQF_SHARED is used as the interrupt flag of + request_irq(). + + + + The last argument of request_irq() is the + data pointer passed to the interrupt handler. Usually, the + chip-specific record is used for that, but you can use what you + like, too. + + + + I won't give details about the interrupt handler at this + point, but at least its appearance can be explained now. The + interrupt handler looks usually like the following: + + + + + + + + + + Now let's write the corresponding destructor for the resources + above. The role of destructor is simple: disable the hardware + (if already activated) and release the resources. So far, we + have no hardware part, so the disabling code is not written here. + + + + To release the resources, the check-and-release + method is a safer way. For the interrupt, do like this: + + + +irq >= 0) + free_irq(chip->irq, chip); +]]> + + + + Since the irq number can start from 0, you should initialize + chip->irq with a negative value (e.g. -1), so that you can + check the validity of the irq number as above. + + + + When you requested I/O ports or memory regions via + pci_request_region() or + pci_request_regions() like in this example, + release the resource(s) using the corresponding function, + pci_release_region() or + pci_release_regions(). + + + +pci); +]]> + + + + + + When you requested manually via request_region() + or request_mem_region, you can release it via + release_resource(). Suppose that you keep + the resource pointer returned from request_region() + in chip->res_port, the release procedure looks like: + + + +res_port); +]]> + + + + + + Don't forget to call pci_disable_device() + before the end. + + + + And finally, release the chip-specific record. + + + + + + + + + + Again, remember that you cannot + use the __devexit prefix for this destructor. + + + + We didn't implement the hardware disabling part in the above. + If you need to do this, please note that the destructor may be + called even before the initialization of the chip is completed. + It would be better to have a flag to skip hardware disabling + if the hardware was not initialized yet. + + + + When the chip-data is assigned to the card using + snd_device_new() with + SNDRV_DEV_LOWLELVEL , its destructor is + called at the last. That is, it is assured that all other + components like PCMs and controls have already been released. + You don't have to stop PCMs, etc. explicitly, but just + call low-level hardware stopping. + + + + The management of a memory-mapped region is almost as same as + the management of an I/O port. You'll need three fields like + the following: + + + + + + + + and the allocation would be like below: + + + +iobase_phys = pci_resource_start(pci, 0); + chip->iobase_virt = ioremap_nocache(chip->iobase_phys, + pci_resource_len(pci, 0)); +]]> + + + + and the corresponding destructor would be: + + + +iobase_virt) + iounmap(chip->iobase_virt); + .... + pci_release_regions(chip->pci); + .... + } +]]> + + + + +
+ +
+ Registration of Device Struct + + At some point, typically after calling snd_device_new(), + you need to register the struct device of the chip + you're handling for udev and co. ALSA provides a macro for compatibility with + older kernels. Simply call like the following: + + +dev); +]]> + + + so that it stores the PCI's device pointer to the card. This will be + referred by ALSA core functions later when the devices are registered. + + + In the case of non-PCI, pass the proper device struct pointer of the BUS + instead. (In the case of legacy ISA without PnP, you don't have to do + anything.) + +
+ +
+ PCI Entries + + So far, so good. Let's finish the missing PCI + stuff. At first, we need a + pci_device_id table for this + chipset. It's a table of PCI vendor/device ID number, and some + masks. + + + + For example, + + + + + + + + + + The first and second fields of + the pci_device_id structure are the vendor and + device IDs. If you have no reason to filter the matching + devices, you can leave the remaining fields as above. The last + field of the pci_device_id struct contains + private data for this entry. You can specify any value here, for + example, to define specific operations for supported device IDs. + Such an example is found in the intel8x0 driver. + + + + The last entry of this list is the terminator. You must + specify this all-zero entry. + + + + Then, prepare the pci_driver record: + + + + + + + + + + The probe and + remove functions have already + been defined in the previous sections. + The remove function should + be defined with the + __devexit_p() macro, so that it's not + defined for built-in (and non-hot-pluggable) case. The + name + field is the name string of this device. Note that you must not + use a slash / in this string. + + + + And at last, the module entries: + + + + + + + + + + Note that these module entries are tagged with + __init and + __exit prefixes, not + __devinit nor + __devexit. + + + + Oh, one thing was forgotten. If you have no exported symbols, + you need to declare it in 2.2 or 2.4 kernels (it's not necessary in 2.6 kernels). + + + + + + + + That's all! + +
+
+ + + + + + + PCM Interface + +
+ General + + The PCM middle layer of ALSA is quite powerful and it is only + necessary for each driver to implement the low-level functions + to access its hardware. + + + + For accessing to the PCM layer, you need to include + <sound/pcm.h> first. In addition, + <sound/pcm_params.h> might be needed + if you access to some functions related with hw_param. + + + + Each card device can have up to four pcm instances. A pcm + instance corresponds to a pcm device file. The limitation of + number of instances comes only from the available bit size of + the Linux's device numbers. Once when 64bit device number is + used, we'll have more pcm instances available. + + + + A pcm instance consists of pcm playback and capture streams, + and each pcm stream consists of one or more pcm substreams. Some + soundcards support multiple playback functions. For example, + emu10k1 has a PCM playback of 32 stereo substreams. In this case, at + each open, a free substream is (usually) automatically chosen + and opened. Meanwhile, when only one substream exists and it was + already opened, the successful open will either block + or error with EAGAIN according to the + file open mode. But you don't have to care about such details in your + driver. The PCM middle layer will take care of such work. + +
+ +
+ Full Code Example + + The example code below does not include any hardware access + routines but shows only the skeleton, how to build up the PCM + interfaces. + + + PCM Example Code + + + .... + + /* hardware definition */ + static struct snd_pcm_hardware snd_mychip_playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, + }; + + /* hardware definition */ + static struct snd_pcm_hardware snd_mychip_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 32768, + .period_bytes_min = 4096, + .period_bytes_max = 32768, + .periods_min = 1, + .periods_max = 1024, + }; + + /* open callback */ + static int snd_mychip_playback_open(struct snd_pcm_substream *substream) + { + struct mychip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw = snd_mychip_playback_hw; + /* more hardware-initialization will be done here */ + .... + return 0; + } + + /* close callback */ + static int snd_mychip_playback_close(struct snd_pcm_substream *substream) + { + struct mychip *chip = snd_pcm_substream_chip(substream); + /* the hardware-specific codes will be here */ + .... + return 0; + + } + + /* open callback */ + static int snd_mychip_capture_open(struct snd_pcm_substream *substream) + { + struct mychip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw = snd_mychip_capture_hw; + /* more hardware-initialization will be done here */ + .... + return 0; + } + + /* close callback */ + static int snd_mychip_capture_close(struct snd_pcm_substream *substream) + { + struct mychip *chip = snd_pcm_substream_chip(substream); + /* the hardware-specific codes will be here */ + .... + return 0; + + } + + /* hw_params callback */ + static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) + { + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + } + + /* hw_free callback */ + static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream) + { + return snd_pcm_lib_free_pages(substream); + } + + /* prepare callback */ + static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream) + { + struct mychip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + /* set up the hardware with the current configuration + * for example... + */ + mychip_set_sample_format(chip, runtime->format); + mychip_set_sample_rate(chip, runtime->rate); + mychip_set_channels(chip, runtime->channels); + mychip_set_dma_setup(chip, runtime->dma_addr, + chip->buffer_size, + chip->period_size); + return 0; + } + + /* trigger callback */ + static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) + { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* do something to start the PCM engine */ + .... + break; + case SNDRV_PCM_TRIGGER_STOP: + /* do something to stop the PCM engine */ + .... + break; + default: + return -EINVAL; + } + } + + /* pointer callback */ + static snd_pcm_uframes_t + snd_mychip_pcm_pointer(struct snd_pcm_substream *substream) + { + struct mychip *chip = snd_pcm_substream_chip(substream); + unsigned int current_ptr; + + /* get the current hardware pointer */ + current_ptr = mychip_get_hw_pointer(chip); + return current_ptr; + } + + /* operators */ + static struct snd_pcm_ops snd_mychip_playback_ops = { + .open = snd_mychip_playback_open, + .close = snd_mychip_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mychip_pcm_hw_params, + .hw_free = snd_mychip_pcm_hw_free, + .prepare = snd_mychip_pcm_prepare, + .trigger = snd_mychip_pcm_trigger, + .pointer = snd_mychip_pcm_pointer, + }; + + /* operators */ + static struct snd_pcm_ops snd_mychip_capture_ops = { + .open = snd_mychip_capture_open, + .close = snd_mychip_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mychip_pcm_hw_params, + .hw_free = snd_mychip_pcm_hw_free, + .prepare = snd_mychip_pcm_prepare, + .trigger = snd_mychip_pcm_trigger, + .pointer = snd_mychip_pcm_pointer, + }; + + /* + * definitions of capture are omitted here... + */ + + /* create a pcm device */ + static int __devinit snd_mychip_new_pcm(struct mychip *chip) + { + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm); + if (err < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, "My Chip"); + chip->pcm = pcm; + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_mychip_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_mychip_capture_ops); + /* pre-allocation of buffers */ + /* NOTE: this may fail */ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 64*1024, 64*1024); + return 0; + } +]]> + + + +
+ +
+ Constructor + + A pcm instance is allocated by the snd_pcm_new() + function. It would be better to create a constructor for pcm, + namely, + + + +card, "My Chip", 0, 1, 1, &pcm); + if (err < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, "My Chip"); + chip->pcm = pcm; + .... + return 0; + } +]]> + + + + + + The snd_pcm_new() function takes four + arguments. The first argument is the card pointer to which this + pcm is assigned, and the second is the ID string. + + + + The third argument (index, 0 in the + above) is the index of this new pcm. It begins from zero. If + you create more than one pcm instances, specify the + different numbers in this argument. For example, + index = 1 for the second PCM device. + + + + The fourth and fifth arguments are the number of substreams + for playback and capture, respectively. Here 1 is used for + both arguments. When no playback or capture substreams are available, + pass 0 to the corresponding argument. + + + + If a chip supports multiple playbacks or captures, you can + specify more numbers, but they must be handled properly in + open/close, etc. callbacks. When you need to know which + substream you are referring to, then it can be obtained from + struct snd_pcm_substream data passed to each callback + as follows: + + + +number; +]]> + + + + + + After the pcm is created, you need to set operators for each + pcm stream. + + + + + + + + + + The operators are defined typically like this: + + + + + + + + All the callbacks are described in the + + Operators subsection. + + + + After setting the operators, you probably will want to + pre-allocate the buffer. For the pre-allocation, simply call + the following: + + + +pci), + 64*1024, 64*1024); +]]> + + + + It will allocate a buffer up to 64kB as default. + Buffer management details will be described in the later section Buffer and Memory + Management. + + + + Additionally, you can set some extra information for this pcm + in pcm->info_flags. + The available values are defined as + SNDRV_PCM_INFO_XXX in + <sound/asound.h>, which is used for + the hardware definition (described later). When your soundchip + supports only half-duplex, specify like this: + + + +info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; +]]> + + + +
+ +
+ ... And the Destructor? + + The destructor for a pcm instance is not always + necessary. Since the pcm device will be released by the middle + layer code automatically, you don't have to call the destructor + explicitly. + + + + The destructor would be necessary if you created + special records internally and needed to release them. In such a + case, set the destructor function to + pcm->private_free: + + + PCM Instance with a Destructor + +my_private_pcm_data); + /* do what you like else */ + .... + } + + static int __devinit snd_mychip_new_pcm(struct mychip *chip) + { + struct snd_pcm *pcm; + .... + /* allocate your own data */ + chip->my_private_pcm_data = kmalloc(...); + /* set the destructor */ + pcm->private_data = chip; + pcm->private_free = mychip_pcm_free; + .... + } +]]> + + + +
+ +
+ Runtime Pointer - The Chest of PCM Information + + When the PCM substream is opened, a PCM runtime instance is + allocated and assigned to the substream. This pointer is + accessible via substream->runtime. + This runtime pointer holds most information you need + to control the PCM: the copy of hw_params and sw_params configurations, the buffer + pointers, mmap records, spinlocks, etc. + + + + The definition of runtime instance is found in + <sound/pcm.h>. Here are + the contents of this file: + + + + + + + + + For the operators (callbacks) of each sound driver, most of + these records are supposed to be read-only. Only the PCM + middle-layer changes / updates them. The exceptions are + the hardware description (hw), interrupt callbacks + (transfer_ack_xxx), DMA buffer information, and the private + data. Besides, if you use the standard buffer allocation + method via snd_pcm_lib_malloc_pages(), + you don't need to set the DMA buffer information by yourself. + + + + In the sections below, important records are explained. + + +
+ Hardware Description + + The hardware descriptor (struct snd_pcm_hardware) + contains the definitions of the fundamental hardware + configuration. Above all, you'll need to define this in + + the open callback. + Note that the runtime instance holds the copy of the + descriptor, not the pointer to the existing descriptor. That + is, in the open callback, you can modify the copied descriptor + (runtime->hw) as you need. For example, if the maximum + number of channels is 1 only on some chip models, you can + still use the same hardware descriptor and change the + channels_max later: + + +runtime; + ... + runtime->hw = snd_mychip_playback_hw; /* common definition */ + if (chip->model == VERY_OLD_ONE) + runtime->hw.channels_max = 1; +]]> + + + + + + Typically, you'll have a hardware descriptor as below: + + + + + + + + + + + The info field contains the type and + capabilities of this pcm. The bit flags are defined in + <sound/asound.h> as + SNDRV_PCM_INFO_XXX. Here, at least, you + have to specify whether the mmap is supported and which + interleaved format is supported. + When the is supported, add the + SNDRV_PCM_INFO_MMAP flag here. When the + hardware supports the interleaved or the non-interleaved + formats, SNDRV_PCM_INFO_INTERLEAVED or + SNDRV_PCM_INFO_NONINTERLEAVED flag must + be set, respectively. If both are supported, you can set both, + too. + + + + In the above example, MMAP_VALID and + BLOCK_TRANSFER are specified for the OSS mmap + mode. Usually both are set. Of course, + MMAP_VALID is set only if the mmap is + really supported. + + + + The other possible flags are + SNDRV_PCM_INFO_PAUSE and + SNDRV_PCM_INFO_RESUME. The + PAUSE bit means that the pcm supports the + pause operation, while the + RESUME bit means that the pcm supports + the full suspend/resume operation. + If the PAUSE flag is set, + the trigger callback below + must handle the corresponding (pause push/release) commands. + The suspend/resume trigger commands can be defined even without + the RESUME flag. See + Power Management section for details. + + + + When the PCM substreams can be synchronized (typically, + synchronized start/stop of a playback and a capture streams), + you can give SNDRV_PCM_INFO_SYNC_START, + too. In this case, you'll need to check the linked-list of + PCM substreams in the trigger callback. This will be + described in the later section. + + + + + + formats field contains the bit-flags + of supported formats (SNDRV_PCM_FMTBIT_XXX). + If the hardware supports more than one format, give all or'ed + bits. In the example above, the signed 16bit little-endian + format is specified. + + + + + + rates field contains the bit-flags of + supported rates (SNDRV_PCM_RATE_XXX). + When the chip supports continuous rates, pass + CONTINUOUS bit additionally. + The pre-defined rate bits are provided only for typical + rates. If your chip supports unconventional rates, you need to add + the KNOT bit and set up the hardware + constraint manually (explained later). + + + + + + rate_min and + rate_max define the minimum and + maximum sample rate. This should correspond somehow to + rates bits. + + + + + + channel_min and + channel_max + define, as you might already expected, the minimum and maximum + number of channels. + + + + + + buffer_bytes_max defines the + maximum buffer size in bytes. There is no + buffer_bytes_min field, since + it can be calculated from the minimum period size and the + minimum number of periods. + Meanwhile, period_bytes_min and + define the minimum and maximum size of the period in bytes. + periods_max and + periods_min define the maximum and + minimum number of periods in the buffer. + + + + The period is a term that corresponds to + a fragment in the OSS world. The period defines the size at + which a PCM interrupt is generated. This size strongly + depends on the hardware. + Generally, the smaller period size will give you more + interrupts, that is, more controls. + In the case of capture, this size defines the input latency. + On the other hand, the whole buffer size defines the + output latency for the playback direction. + + + + + + There is also a field fifo_size. + This specifies the size of the hardware FIFO, but currently it + is neither used in the driver nor in the alsa-lib. So, you + can ignore this field. + + + + +
+ +
+ PCM Configurations + + Ok, let's go back again to the PCM runtime records. + The most frequently referred records in the runtime instance are + the PCM configurations. + The PCM configurations are stored in the runtime instance + after the application sends hw_params data via + alsa-lib. There are many fields copied from hw_params and + sw_params structs. For example, + format holds the format type + chosen by the application. This field contains the enum value + SNDRV_PCM_FORMAT_XXX. + + + + One thing to be noted is that the configured buffer and period + sizes are stored in frames in the runtime. + In the ALSA world, 1 frame = channels * samples-size. + For conversion between frames and bytes, you can use the + frames_to_bytes() and + bytes_to_frames() helper functions. + + +period_size); +]]> + + + + + + Also, many software parameters (sw_params) are + stored in frames, too. Please check the type of the field. + snd_pcm_uframes_t is for the frames as unsigned + integer while snd_pcm_sframes_t is for the frames + as signed integer. + +
+ +
+ DMA Buffer Information + + The DMA buffer is defined by the following four fields, + dma_area, + dma_addr, + dma_bytes and + dma_private. + The dma_area holds the buffer + pointer (the logical address). You can call + memcpy from/to + this pointer. Meanwhile, dma_addr + holds the physical address of the buffer. This field is + specified only when the buffer is a linear buffer. + dma_bytes holds the size of buffer + in bytes. dma_private is used for + the ALSA DMA allocator. + + + + If you use a standard ALSA function, + snd_pcm_lib_malloc_pages(), for + allocating the buffer, these fields are set by the ALSA middle + layer, and you should not change them by + yourself. You can read them but not write them. + On the other hand, if you want to allocate the buffer by + yourself, you'll need to manage it in hw_params callback. + At least, dma_bytes is mandatory. + dma_area is necessary when the + buffer is mmapped. If your driver doesn't support mmap, this + field is not necessary. dma_addr + is also optional. You can use + dma_private as you like, too. + +
+ +
+ Running Status + + The running status can be referred via runtime->status. + This is the pointer to the struct snd_pcm_mmap_status + record. For example, you can get the current DMA hardware + pointer via runtime->status->hw_ptr. + + + + The DMA application pointer can be referred via + runtime->control, which points to the + struct snd_pcm_mmap_control record. + However, accessing directly to this value is not recommended. + +
+ +
+ Private Data + + You can allocate a record for the substream and store it in + runtime->private_data. Usually, this + is done in + + the open callback. + Don't mix this with pcm->private_data. + The pcm->private_data usually points to the + chip instance assigned statically at the creation of PCM, while the + runtime->private_data points to a dynamic + data structure created at the PCM open callback. + + + +runtime->private_data = data; + .... + } +]]> + + + + + + The allocated object must be released in + + the close callback. + +
+ +
+ Interrupt Callbacks + + The field transfer_ack_begin and + transfer_ack_end are called at + the beginning and at the end of + snd_pcm_period_elapsed(), respectively. + +
+ +
+ +
+ Operators + + OK, now let me give details about each pcm callback + (ops). In general, every callback must + return 0 if successful, or a negative error number + such as -EINVAL. To choose an appropriate + error number, it is advised to check what value other parts of + the kernel return when the same kind of request fails. + + + + The callback function takes at least the argument with + snd_pcm_substream pointer. To retrieve + the chip record from the given substream instance, you can use the + following macro. + + + + + + + + The macro reads substream->private_data, + which is a copy of pcm->private_data. + You can override the former if you need to assign different data + records per PCM substream. For example, the cmi8330 driver assigns + different private_data for playback and capture directions, + because it uses two different codecs (SB- and AD-compatible) for + different directions. + + +
+ open callback + + + + + + + + This is called when a pcm substream is opened. + + + + At least, here you have to initialize the runtime->hw + record. Typically, this is done by like this: + + + +runtime; + + runtime->hw = snd_mychip_playback_hw; + return 0; + } +]]> + + + + where snd_mychip_playback_hw is the + pre-defined hardware description. + + + + You can allocate a private data in this callback, as described + in + Private Data section. + + + + If the hardware configuration needs more constraints, set the + hardware constraints here, too. + See + Constraints for more details. + +
+ +
+ close callback + + + + + + + + Obviously, this is called when a pcm substream is closed. + + + + Any private instance for a pcm substream allocated in the + open callback will be released here. + + + +runtime->private_data); + .... + } +]]> + + + +
+ +
+ ioctl callback + + This is used for any special call to pcm ioctls. But + usually you can pass a generic ioctl callback, + snd_pcm_lib_ioctl. + +
+ +
+ hw_params callback + + + + + + + + + + This is called when the hardware parameter + (hw_params) is set + up by the application, + that is, once when the buffer size, the period size, the + format, etc. are defined for the pcm substream. + + + + Many hardware setups should be done in this callback, + including the allocation of buffers. + + + + Parameters to be initialized are retrieved by + params_xxx() macros. To allocate + buffer, you can call a helper function, + + + + + + + + snd_pcm_lib_malloc_pages() is available + only when the DMA buffers have been pre-allocated. + See the section + Buffer Types for more details. + + + + Note that this and prepare callbacks + may be called multiple times per initialization. + For example, the OSS emulation may + call these callbacks at each change via its ioctl. + + + + Thus, you need to be careful not to allocate the same buffers + many times, which will lead to memory leaks! Calling the + helper function above many times is OK. It will release the + previous buffer automatically when it was already allocated. + + + + Another note is that this callback is non-atomic + (schedulable). This is important, because the + trigger callback + is atomic (non-schedulable). That is, mutexes or any + schedule-related functions are not available in + trigger callback. + Please see the subsection + + Atomicity for details. + +
+ +
+ hw_free callback + + + + + + + + + + This is called to release the resources allocated via + hw_params. For example, releasing the + buffer via + snd_pcm_lib_malloc_pages() is done by + calling the following: + + + + + + + + + + This function is always called before the close callback is called. + Also, the callback may be called multiple times, too. + Keep track whether the resource was already released. + +
+ +
+ prepare callback + + + + + + + + + + This callback is called when the pcm is + prepared. You can set the format type, sample + rate, etc. here. The difference from + hw_params is that the + prepare callback will be called each + time + snd_pcm_prepare() is called, i.e. when + recovering after underruns, etc. + + + + Note that this callback is now non-atomic. + You can use schedule-related functions safely in this callback. + + + + In this and the following callbacks, you can refer to the + values via the runtime record, + substream->runtime. + For example, to get the current + rate, format or channels, access to + runtime->rate, + runtime->format or + runtime->channels, respectively. + The physical address of the allocated buffer is set to + runtime->dma_area. The buffer and period sizes are + in runtime->buffer_size and runtime->period_size, + respectively. + + + + Be careful that this callback will be called many times at + each setup, too. + +
+ +
+ trigger callback + + + + + + + + This is called when the pcm is started, stopped or paused. + + + + Which action is specified in the second argument, + SNDRV_PCM_TRIGGER_XXX in + <sound/pcm.h>. At least, + the START and STOP + commands must be defined in this callback. + + + + + + + + + + When the pcm supports the pause operation (given in the info + field of the hardware table), the PAUSE_PUSE + and PAUSE_RELEASE commands must be + handled here, too. The former is the command to pause the pcm, + and the latter to restart the pcm again. + + + + When the pcm supports the suspend/resume operation, + regardless of full or partial suspend/resume support, + the SUSPEND and RESUME + commands must be handled, too. + These commands are issued when the power-management status is + changed. Obviously, the SUSPEND and + RESUME commands + suspend and resume the pcm substream, and usually, they + are identical to the STOP and + START commands, respectively. + See the + Power Management section for details. + + + + As mentioned, this callback is atomic. You cannot call + functions which may sleep. + The trigger callback should be as minimal as possible, + just really triggering the DMA. The other stuff should be + initialized hw_params and prepare callbacks properly + beforehand. + +
+ +
+ pointer callback + + + + + + + + This callback is called when the PCM middle layer inquires + the current hardware position on the buffer. The position must + be returned in frames, + ranging from 0 to buffer_size - 1. + + + + This is called usually from the buffer-update routine in the + pcm middle layer, which is invoked when + snd_pcm_period_elapsed() is called in the + interrupt routine. Then the pcm middle layer updates the + position and calculates the available space, and wakes up the + sleeping poll threads, etc. + + + + This callback is also atomic. + +
+ +
+ copy and silence callbacks + + These callbacks are not mandatory, and can be omitted in + most cases. These callbacks are used when the hardware buffer + cannot be in the normal memory space. Some chips have their + own buffer on the hardware which is not mappable. In such a + case, you have to transfer the data manually from the memory + buffer to the hardware buffer. Or, if the buffer is + non-contiguous on both physical and virtual memory spaces, + these callbacks must be defined, too. + + + + If these two callbacks are defined, copy and set-silence + operations are done by them. The detailed will be described in + the later section Buffer and Memory + Management. + +
+ +
+ ack callback + + This callback is also not mandatory. This callback is called + when the appl_ptr is updated in read or write operations. + Some drivers like emu10k1-fx and cs46xx need to track the + current appl_ptr for the internal buffer, and this callback + is useful only for such a purpose. + + + This callback is atomic. + +
+ +
+ page callback + + + This callback is optional too. This callback is used + mainly for non-contiguous buffers. The mmap calls this + callback to get the page address. Some examples will be + explained in the later section Buffer and Memory + Management, too. + +
+
+ +
+ Interrupt Handler + + The rest of pcm stuff is the PCM interrupt handler. The + role of PCM interrupt handler in the sound driver is to update + the buffer position and to tell the PCM middle layer when the + buffer position goes across the prescribed period size. To + inform this, call the snd_pcm_period_elapsed() + function. + + + + There are several types of sound chips to generate the interrupts. + + +
+ Interrupts at the period (fragment) boundary + + This is the most frequently found type: the hardware + generates an interrupt at each period boundary. + In this case, you can call + snd_pcm_period_elapsed() at each + interrupt. + + + + snd_pcm_period_elapsed() takes the + substream pointer as its argument. Thus, you need to keep the + substream pointer accessible from the chip instance. For + example, define substream field in the chip record to hold the + current running substream pointer, and set the pointer value + at open callback (and reset at close callback). + + + + If you acquire a spinlock in the interrupt handler, and the + lock is used in other pcm callbacks, too, then you have to + release the lock before calling + snd_pcm_period_elapsed(), because + snd_pcm_period_elapsed() calls other pcm + callbacks inside. + + + + Typical code would be like: + + + Interrupt Handler Case #1 + +lock); + .... + if (pcm_irq_invoked(chip)) { + /* call updater, unlock before it */ + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(chip->substream); + spin_lock(&chip->lock); + /* acknowledge the interrupt if necessary */ + } + .... + spin_unlock(&chip->lock); + return IRQ_HANDLED; + } +]]> + + + +
+ +
+ High frequency timer interrupts + + This happense when the hardware doesn't generate interrupts + at the period boundary but issues timer interrupts at a fixed + timer rate (e.g. es1968 or ymfpci drivers). + In this case, you need to check the current hardware + position and accumulate the processed sample length at each + interrupt. When the accumulated size exceeds the period + size, call + snd_pcm_period_elapsed() and reset the + accumulator. + + + + Typical code would be like the following. + + + Interrupt Handler Case #2 + +lock); + .... + if (pcm_irq_invoked(chip)) { + unsigned int last_ptr, size; + /* get the current hardware pointer (in frames) */ + last_ptr = get_hw_ptr(chip); + /* calculate the processed frames since the + * last update + */ + if (last_ptr < chip->last_ptr) + size = runtime->buffer_size + last_ptr + - chip->last_ptr; + else + size = last_ptr - chip->last_ptr; + /* remember the last updated point */ + chip->last_ptr = last_ptr; + /* accumulate the size */ + chip->size += size; + /* over the period boundary? */ + if (chip->size >= runtime->period_size) { + /* reset the accumulator */ + chip->size %= runtime->period_size; + /* call updater */ + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->lock); + } + /* acknowledge the interrupt if necessary */ + } + .... + spin_unlock(&chip->lock); + return IRQ_HANDLED; + } +]]> + + + +
+ +
+ On calling <function>snd_pcm_period_elapsed()</function> + + In both cases, even if more than one period are elapsed, you + don't have to call + snd_pcm_period_elapsed() many times. Call + only once. And the pcm layer will check the current hardware + pointer and update to the latest status. + +
+
+ +
+ Atomicity + + One of the most important (and thus difficult to debug) problems + in kernel programming are race conditions. + In the Linux kernel, they are usually avoided via spin-locks, mutexes + or semaphores. In general, if a race condition can happen + in an interrupt handler, it has to be managed atomically, and you + have to use a spinlock to protect the critical session. If the + critical section is not in interrupt handler code and + if taking a relatively long time to execute is acceptable, you + should use mutexes or semaphores instead. + + + + As already seen, some pcm callbacks are atomic and some are + not. For example, the hw_params callback is + non-atomic, while trigger callback is + atomic. This means, the latter is called already in a spinlock + held by the PCM middle layer. Please take this atomicity into + account when you choose a locking scheme in the callbacks. + + + + In the atomic callbacks, you cannot use functions which may call + schedule or go to + sleep. Semaphores and mutexes can sleep, + and hence they cannot be used inside the atomic callbacks + (e.g. trigger callback). + To implement some delay in such a callback, please use + udelay() or mdelay(). + + + + All three atomic callbacks (trigger, pointer, and ack) are + called with local interrupts disabled. + + +
+
+ Constraints + + If your chip supports unconventional sample rates, or only the + limited samples, you need to set a constraint for the + condition. + + + + For example, in order to restrict the sample rates in the some + supported values, use + snd_pcm_hw_constraint_list(). + You need to call this function in the open callback. + + + Example of Hardware Constraints + +runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + if (err < 0) + return err; + .... + } +]]> + + + + + + There are many different constraints. + Look at sound/pcm.h for a complete list. + You can even define your own constraint rules. + For example, let's suppose my_chip can manage a substream of 1 channel + if and only if the format is S16_LE, otherwise it supports any format + specified in the snd_pcm_hardware structure (or in any + other constraint_list). You can build a rule like this: + + + Example of Hardware Constraints for Channels + +min < 2) { + fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE; + return snd_mask_refine(f, &fmt); + } + return 0; + } +]]> + + + + + + Then you need to call this function to add your rule: + + + +runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels_by_format, 0, SNDRV_PCM_HW_PARAM_FORMAT, + -1); +]]> + + + + + + The rule function is called when an application sets the number of + channels. But an application can set the format before the number of + channels. Thus you also need to define the inverse rule: + + + Example of Hardware Constraints for Channels + +bits[0] == SNDRV_PCM_FMTBIT_S16_LE) { + ch.min = ch.max = 1; + ch.integer = 1; + return snd_interval_refine(c, &ch); + } + return 0; + } +]]> + + + + + + ...and in the open callback: + + +runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format_by_channels, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + -1); +]]> + + + + + + I won't give more details here, rather I + would like to say, Luke, use the source. + +
+ +
+ + + + + + + Control Interface + +
+ General + + The control interface is used widely for many switches, + sliders, etc. which are accessed from user-space. Its most + important use is the mixer interface. In other words, since ALSA + 0.9.x, all the mixer stuff is implemented on the control kernel API. + + + + ALSA has a well-defined AC97 control module. If your chip + supports only the AC97 and nothing else, you can skip this + section. + + + + The control API is defined in + <sound/control.h>. + Include this file if you want to add your own controls. + +
+ +
+ Definition of Controls + + To create a new control, you need to define the + following three + callbacks: info, + get and + put. Then, define a + struct snd_kcontrol_new record, such as: + + + Definition of a Control + + + + + + + + Most likely the control is created via + snd_ctl_new1(), and in such a case, you can + add the __devinitdata prefix to the + definition as above. + + + + The iface field specifies the control + type, SNDRV_CTL_ELEM_IFACE_XXX, which + is usually MIXER. + Use CARD for global controls that are not + logically part of the mixer. + If the control is closely associated with some specific device on + the sound card, use HWDEP, + PCM, RAWMIDI, + TIMER, or SEQUENCER, and + specify the device number with the + device and + subdevice fields. + + + + The name is the name identifier + string. Since ALSA 0.9.x, the control name is very important, + because its role is classified from its name. There are + pre-defined standard control names. The details are described in + the + Control Names subsection. + + + + The index field holds the index number + of this control. If there are several different controls with + the same name, they can be distinguished by the index + number. This is the case when + several codecs exist on the card. If the index is zero, you can + omit the definition above. + + + + The access field contains the access + type of this control. Give the combination of bit masks, + SNDRV_CTL_ELEM_ACCESS_XXX, there. + The details will be explained in + the + Access Flags subsection. + + + + The private_value field contains + an arbitrary long integer value for this record. When using + the generic info, + get and + put callbacks, you can pass a value + through this field. If several small numbers are necessary, you can + combine them in bitwise. Or, it's possible to give a pointer + (casted to unsigned long) of some record to this field, too. + + + + The tlv field can be used to provide + metadata about the control; see the + + Metadata subsection. + + + + The other three are + + callback functions. + +
+ +
+ Control Names + + There are some standards to define the control names. A + control is usually defined from the three parts as + SOURCE DIRECTION FUNCTION. + + + + The first, SOURCE, specifies the source + of the control, and is a string such as Master, + PCM, CD and + Line. There are many pre-defined sources. + + + + The second, DIRECTION, is one of the + following strings according to the direction of the control: + Playback, Capture, Bypass + Playback and Bypass Capture. Or, it can + be omitted, meaning both playback and capture directions. + + + + The third, FUNCTION, is one of the + following strings according to the function of the control: + Switch, Volume and + Route. + + + + The example of control names are, thus, Master Capture + Switch or PCM Playback Volume. + + + + There are some exceptions: + + +
+ Global capture and playback + + Capture Source, Capture Switch + and Capture Volume are used for the global + capture (input) source, switch and volume. Similarly, + Playback Switch and Playback + Volume are used for the global output gain switch and + volume. + +
+ +
+ Tone-controls + + tone-control switch and volumes are specified like + Tone Control - XXX, e.g. Tone Control - + Switch, Tone Control - Bass, + Tone Control - Center. + +
+ +
+ 3D controls + + 3D-control switches and volumes are specified like 3D + Control - XXX, e.g. 3D Control - + Switch, 3D Control - Center, 3D + Control - Space. + +
+ +
+ Mic boost + + Mic-boost switch is set as Mic Boost or + Mic Boost (6dB). + + + + More precise information can be found in + Documentation/sound/alsa/ControlNames.txt. + +
+
+ +
+ Access Flags + + + The access flag is the bitmask which specifies the access type + of the given control. The default access type is + SNDRV_CTL_ELEM_ACCESS_READWRITE, + which means both read and write are allowed to this control. + When the access flag is omitted (i.e. = 0), it is + considered as READWRITE access as default. + + + + When the control is read-only, pass + SNDRV_CTL_ELEM_ACCESS_READ instead. + In this case, you don't have to define + the put callback. + Similarly, when the control is write-only (although it's a rare + case), you can use the WRITE flag instead, and + you don't need the get callback. + + + + If the control value changes frequently (e.g. the VU meter), + VOLATILE flag should be given. This means + that the control may be changed without + + notification. Applications should poll such + a control constantly. + + + + When the control is inactive, set + the INACTIVE flag, too. + There are LOCK and + OWNER flags to change the write + permissions. + + +
+ +
+ Callbacks + +
+ info callback + + The info callback is used to get + detailed information on this control. This must store the + values of the given struct snd_ctl_elem_info + object. For example, for a boolean control with a single + element: + + + Example of info callback + +type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; + } +]]> + + + + + + The type field specifies the type + of the control. There are BOOLEAN, + INTEGER, ENUMERATED, + BYTES, IEC958 and + INTEGER64. The + count field specifies the + number of elements in this control. For example, a stereo + volume would have count = 2. The + value field is a union, and + the values stored are depending on the type. The boolean and + integer types are identical. + + + + The enumerated type is a bit different from others. You'll + need to set the string for the currently given item index. + + + +type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; + } +]]> + + + + + + Some common info callbacks are available for your convenience: + snd_ctl_boolean_mono_info() and + snd_ctl_boolean_stereo_info(). + Obviously, the former is an info callback for a mono channel + boolean item, just like snd_myctl_mono_info + above, and the latter is for a stereo channel boolean item. + + +
+ +
+ get callback + + + This callback is used to read the current value of the + control and to return to user-space. + + + + For example, + + + Example of get callback + +value.integer.value[0] = get_some_value(chip); + return 0; + } +]]> + + + + + + The value field depends on + the type of control as well as on the info callback. For example, + the sb driver uses this field to store the register offset, + the bit-shift and the bit-mask. The + private_value field is set as follows: + + + + + + and is retrieved in callbacks like + + +private_value & 0xff; + int shift = (kcontrol->private_value >> 16) & 0xff; + int mask = (kcontrol->private_value >> 24) & 0xff; + .... + } +]]> + + + + + + In the get callback, + you have to fill all the elements if the + control has more than one elements, + i.e. count > 1. + In the example above, we filled only one element + (value.integer.value[0]) since it's + assumed as count = 1. + +
+ +
+ put callback + + + This callback is used to write a value from user-space. + + + + For example, + + + Example of put callback + +current_value != + ucontrol->value.integer.value[0]) { + change_current_value(chip, + ucontrol->value.integer.value[0]); + changed = 1; + } + return changed; + } +]]> + + + + As seen above, you have to return 1 if the value is + changed. If the value is not changed, return 0 instead. + If any fatal error happens, return a negative error code as + usual. + + + + As in the get callback, + when the control has more than one elements, + all elements must be evaluated in this callback, too. + +
+ +
+ Callbacks are not atomic + + All these three callbacks are basically not atomic. + +
+
+ +
+ Constructor + + When everything is ready, finally we can create a new + control. To create a control, there are two functions to be + called, snd_ctl_new1() and + snd_ctl_add(). + + + + In the simplest way, you can do like this: + + + + + + + + where my_control is the + struct snd_kcontrol_new object defined above, and chip + is the object pointer to be passed to + kcontrol->private_data + which can be referred to in callbacks. + + + + snd_ctl_new1() allocates a new + snd_kcontrol instance (that's why the definition + of my_control can be with + the __devinitdata + prefix), and snd_ctl_add assigns the given + control component to the card. + +
+ +
+ Change Notification + + If you need to change and update a control in the interrupt + routine, you can call snd_ctl_notify(). For + example, + + + + + + + + This function takes the card pointer, the event-mask, and the + control id pointer for the notification. The event-mask + specifies the types of notification, for example, in the above + example, the change of control values is notified. + The id pointer is the pointer of struct snd_ctl_elem_id + to be notified. + You can find some examples in es1938.c or + es1968.c for hardware volume interrupts. + +
+ +
+ Metadata + + To provide information about the dB values of a mixer control, use + on of the DECLARE_TLV_xxx macros from + <sound/tlv.h> to define a variable + containing this information, set thetlv.p + field to point to this variable, and include the + SNDRV_CTL_ELEM_ACCESS_TLV_READ flag in the + access field; like this: + + + + + + + + + The DECLARE_TLV_DB_SCALE macro defines + information about a mixer control where each step in the control's + value changes the dB value by a constant dB amount. + The first parameter is the name of the variable to be defined. + The second parameter is the minimum value, in units of 0.01 dB. + The third parameter is the step size, in units of 0.01 dB. + Set the fourth parameter to 1 if the minimum value actually mutes + the control. + + + + The DECLARE_TLV_DB_LINEAR macro defines + information about a mixer control where the control's value affects + the output linearly. + The first parameter is the name of the variable to be defined. + The second parameter is the minimum value, in units of 0.01 dB. + The third parameter is the maximum value, in units of 0.01 dB. + If the minimum value mutes the control, set the second parameter to + TLV_DB_GAIN_MUTE. + +
+ +
+ + + + + + + API for AC97 Codec + +
+ General + + The ALSA AC97 codec layer is a well-defined one, and you don't + have to write much code to control it. Only low-level control + routines are necessary. The AC97 codec API is defined in + <sound/ac97_codec.h>. + +
+ +
+ Full Code Example + + + Example of AC97 Interface + +private_data; + .... + /* read a register value here from the codec */ + return the_register_value; + } + + static void snd_mychip_ac97_write(struct snd_ac97 *ac97, + unsigned short reg, unsigned short val) + { + struct mychip *chip = ac97->private_data; + .... + /* write the given register value to the codec */ + } + + static int snd_mychip_ac97(struct mychip *chip) + { + struct snd_ac97_bus *bus; + struct snd_ac97_template ac97; + int err; + static struct snd_ac97_bus_ops ops = { + .write = snd_mychip_ac97_write, + .read = snd_mychip_ac97_read, + }; + + err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus); + if (err < 0) + return err; + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + return snd_ac97_mixer(bus, &ac97, &chip->ac97); + } + +]]> + + + +
+ +
+ Constructor + + To create an ac97 instance, first call snd_ac97_bus + with an ac97_bus_ops_t record with callback functions. + + + + + + + + The bus record is shared among all belonging ac97 instances. + + + + And then call snd_ac97_mixer() with an + struct snd_ac97_template + record together with the bus pointer created above. + + + +ac97); +]]> + + + + where chip->ac97 is a pointer to a newly created + ac97_t instance. + In this case, the chip pointer is set as the private data, so that + the read/write callback functions can refer to this chip instance. + This instance is not necessarily stored in the chip + record. If you need to change the register values from the + driver, or need the suspend/resume of ac97 codecs, keep this + pointer to pass to the corresponding functions. + +
+ +
+ Callbacks + + The standard callbacks are read and + write. Obviously they + correspond to the functions for read and write accesses to the + hardware low-level codes. + + + + The read callback returns the + register value specified in the argument. + + + +private_data; + .... + return the_register_value; + } +]]> + + + + Here, the chip can be cast from ac97->private_data. + + + + Meanwhile, the write callback is + used to set the register value. + + + + + + + + + + These callbacks are non-atomic like the control API callbacks. + + + + There are also other callbacks: + reset, + wait and + init. + + + + The reset callback is used to reset + the codec. If the chip requires a special kind of reset, you can + define this callback. + + + + The wait callback is used to + add some waiting time in the standard initialization of the codec. If the + chip requires the extra waiting time, define this callback. + + + + The init callback is used for + additional initialization of the codec. + +
+ +
+ Updating Registers in The Driver + + If you need to access to the codec from the driver, you can + call the following functions: + snd_ac97_write(), + snd_ac97_read(), + snd_ac97_update() and + snd_ac97_update_bits(). + + + + Both snd_ac97_write() and + snd_ac97_update() functions are used to + set a value to the given register + (AC97_XXX). The difference between them is + that snd_ac97_update() doesn't write a + value if the given value has been already set, while + snd_ac97_write() always rewrites the + value. + + + + + + + + + + snd_ac97_read() is used to read the value + of the given register. For example, + + + + + + + + + + snd_ac97_update_bits() is used to update + some bits in the given register. + + + + + + + + + + Also, there is a function to change the sample rate (of a + given register such as + AC97_PCM_FRONT_DAC_RATE) when VRA or + DRA is supported by the codec: + snd_ac97_set_rate(). + + + + + + + + + + The following registers are available to set the rate: + AC97_PCM_MIC_ADC_RATE, + AC97_PCM_FRONT_DAC_RATE, + AC97_PCM_LR_ADC_RATE, + AC97_SPDIF. When + AC97_SPDIF is specified, the register is + not really changed but the corresponding IEC958 status bits will + be updated. + +
+ +
+ Clock Adjustment + + In some chips, the clock of the codec isn't 48000 but using a + PCI clock (to save a quartz!). In this case, change the field + bus->clock to the corresponding + value. For example, intel8x0 + and es1968 drivers have their own function to read from the clock. + +
+ +
+ Proc Files + + The ALSA AC97 interface will create a proc file such as + /proc/asound/card0/codec97#0/ac97#0-0 and + ac97#0-0+regs. You can refer to these files to + see the current status and registers of the codec. + +
+ +
+ Multiple Codecs + + When there are several codecs on the same card, you need to + call snd_ac97_mixer() multiple times with + ac97.num=1 or greater. The num field + specifies the codec number. + + + + If you set up multiple codecs, you either need to write + different callbacks for each codec or check + ac97->num in the callback routines. + +
+ +
+ + + + + + + MIDI (MPU401-UART) Interface + +
+ General + + Many soundcards have built-in MIDI (MPU401-UART) + interfaces. When the soundcard supports the standard MPU401-UART + interface, most likely you can use the ALSA MPU401-UART API. The + MPU401-UART API is defined in + <sound/mpu401.h>. + + + + Some soundchips have a similar but slightly different + implementation of mpu401 stuff. For example, emu10k1 has its own + mpu401 routines. + +
+ +
+ Constructor + + To create a rawmidi object, call + snd_mpu401_uart_new(). + + + + + + + + + + The first argument is the card pointer, and the second is the + index of this component. You can create up to 8 rawmidi + devices. + + + + The third argument is the type of the hardware, + MPU401_HW_XXX. If it's not a special one, + you can use MPU401_HW_MPU401. + + + + The 4th argument is the I/O port address. Many + backward-compatible MPU401 have an I/O port such as 0x330. Or, it + might be a part of its own PCI I/O region. It depends on the + chip design. + + + + The 5th argument is a bitflag for additional information. + When the I/O port address above is part of the PCI I/O + region, the MPU401 I/O port might have been already allocated + (reserved) by the driver itself. In such a case, pass a bit flag + MPU401_INFO_INTEGRATED, + and the mpu401-uart layer will allocate the I/O ports by itself. + + + + When the controller supports only the input or output MIDI stream, + pass the MPU401_INFO_INPUT or + MPU401_INFO_OUTPUT bitflag, respectively. + Then the rawmidi instance is created as a single stream. + + + + MPU401_INFO_MMIO bitflag is used to change + the access method to MMIO (via readb and writeb) instead of + iob and outb. In this case, you have to pass the iomapped address + to snd_mpu401_uart_new(). + + + + When MPU401_INFO_TX_IRQ is set, the output + stream isn't checked in the default interrupt handler. The driver + needs to call snd_mpu401_uart_interrupt_tx() + by itself to start processing the output stream in the irq handler. + + + + Usually, the port address corresponds to the command port and + port + 1 corresponds to the data port. If not, you may change + the cport field of + struct snd_mpu401 manually + afterward. However, snd_mpu401 pointer is not + returned explicitly by + snd_mpu401_uart_new(). You need to cast + rmidi->private_data to + snd_mpu401 explicitly, + + + +private_data; +]]> + + + + and reset the cport as you like: + + + +cport = my_own_control_port; +]]> + + + + + + The 6th argument specifies the irq number for UART. If the irq + is already allocated, pass 0 to the 7th argument + (irq_flags). Otherwise, pass the flags + for irq allocation + (SA_XXX bits) to it, and the irq will be + reserved by the mpu401-uart layer. If the card doesn't generate + UART interrupts, pass -1 as the irq number. Then a timer + interrupt will be invoked for polling. + +
+ +
+ Interrupt Handler + + When the interrupt is allocated in + snd_mpu401_uart_new(), the private + interrupt handler is used, hence you don't have anything else to do + than creating the mpu401 stuff. Otherwise, you have to call + snd_mpu401_uart_interrupt() explicitly when + a UART interrupt is invoked and checked in your own interrupt + handler. + + + + In this case, you need to pass the private_data of the + returned rawmidi object from + snd_mpu401_uart_new() as the second + argument of snd_mpu401_uart_interrupt(). + + + +private_data, regs); +]]> + + + +
+ +
+ + + + + + + RawMIDI Interface + +
+ Overview + + + The raw MIDI interface is used for hardware MIDI ports that can + be accessed as a byte stream. It is not used for synthesizer + chips that do not directly understand MIDI. + + + + ALSA handles file and buffer management. All you have to do is + to write some code to move data between the buffer and the + hardware. + + + + The rawmidi API is defined in + <sound/rawmidi.h>. + +
+ +
+ Constructor + + + To create a rawmidi device, call the + snd_rawmidi_new function: + + +card, "MyMIDI", 0, outs, ins, &rmidi); + if (err < 0) + return err; + rmidi->private_data = chip; + strcpy(rmidi->name, "My MIDI"); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; +]]> + + + + + + The first argument is the card pointer, the second argument is + the ID string. + + + + The third argument is the index of this component. You can + create up to 8 rawmidi devices. + + + + The fourth and fifth arguments are the number of output and + input substreams, respectively, of this device (a substream is + the equivalent of a MIDI port). + + + + Set the info_flags field to specify + the capabilities of the device. + Set SNDRV_RAWMIDI_INFO_OUTPUT if there is + at least one output port, + SNDRV_RAWMIDI_INFO_INPUT if there is at + least one input port, + and SNDRV_RAWMIDI_INFO_DUPLEX if the device + can handle output and input at the same time. + + + + After the rawmidi device is created, you need to set the + operators (callbacks) for each substream. There are helper + functions to set the operators for all the substreams of a device: + + + + + + + + + The operators are usually defined like this: + + + + + + These callbacks are explained in the Callbacks + section. + + + + If there are more than one substream, you should give a + unique name to each of them: + + +streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams, + list { + sprintf(substream->name, "My MIDI Port %d", substream->number + 1); + } + /* same for SNDRV_RAWMIDI_STREAM_INPUT */ +]]> + + + +
+ +
+ Callbacks + + + In all the callbacks, the private data that you've set for the + rawmidi device can be accessed as + substream->rmidi->private_data. + + + + + If there is more than one port, your callbacks can determine the + port index from the struct snd_rawmidi_substream data passed to each + callback: + + +number; +]]> + + + + +
+ <function>open</function> callback + + + + + + + + + This is called when a substream is opened. + You can initialize the hardware here, but you shouldn't + start transmitting/receiving data yet. + +
+ +
+ <function>close</function> callback + + + + + + + + + Guess what. + + + + The open and close + callbacks of a rawmidi device are serialized with a mutex, + and can sleep. + +
+ +
+ <function>trigger</function> callback for output + substreams + + + + + + + + + This is called with a nonzero up + parameter when there is some data in the substream buffer that + must be transmitted. + + + + To read data from the buffer, call + snd_rawmidi_transmit_peek. It will + return the number of bytes that have been read; this will be + less than the number of bytes requested when there are no more + data in the buffer. + After the data have been transmitted successfully, call + snd_rawmidi_transmit_ack to remove the + data from the substream buffer: + + + + + + + + + If you know beforehand that the hardware will accept data, you + can use the snd_rawmidi_transmit function + which reads some data and removes them from the buffer at once: + + + + + + + + + If you know beforehand how many bytes you can accept, you can + use a buffer size greater than one with the + snd_rawmidi_transmit* functions. + + + + The trigger callback must not sleep. If + the hardware FIFO is full before the substream buffer has been + emptied, you have to continue transmitting data later, either + in an interrupt handler, or with a timer if the hardware + doesn't have a MIDI transmit interrupt. + + + + The trigger callback is called with a + zero up parameter when the transmission + of data should be aborted. + +
+ +
+ <function>trigger</function> callback for input + substreams + + + + + + + + + This is called with a nonzero up + parameter to enable receiving data, or with a zero + up parameter do disable receiving data. + + + + The trigger callback must not sleep; the + actual reading of data from the device is usually done in an + interrupt handler. + + + + When data reception is enabled, your interrupt handler should + call snd_rawmidi_receive for all received + data: + + + + + + +
+ +
+ <function>drain</function> callback + + + + + + + + + This is only used with output substreams. This function should wait + until all data read from the substream buffer have been transmitted. + This ensures that the device can be closed and the driver unloaded + without losing data. + + + + This callback is optional. If you do not set + drain in the struct snd_rawmidi_ops + structure, ALSA will simply wait for 50 milliseconds + instead. + +
+
+ +
+ + + + + + + Miscellaneous Devices + +
+ FM OPL3 + + The FM OPL3 is still used in many chips (mainly for backward + compatibility). ALSA has a nice OPL3 FM control layer, too. The + OPL3 API is defined in + <sound/opl3.h>. + + + + FM registers can be directly accessed through the direct-FM API, + defined in <sound/asound_fm.h>. In + ALSA native mode, FM registers are accessed through + the Hardware-Dependant Device direct-FM extension API, whereas in + OSS compatible mode, FM registers can be accessed with the OSS + direct-FM compatible API in /dev/dmfmX device. + + + + To create the OPL3 component, you have two functions to + call. The first one is a constructor for the opl3_t + instance. + + + + + + + + + + The first argument is the card pointer, the second one is the + left port address, and the third is the right port address. In + most cases, the right port is placed at the left port + 2. + + + + The fourth argument is the hardware type. + + + + When the left and right ports have been already allocated by + the card driver, pass non-zero to the fifth argument + (integrated). Otherwise, the opl3 module will + allocate the specified ports by itself. + + + + When the accessing the hardware requires special method + instead of the standard I/O access, you can create opl3 instance + separately with snd_opl3_new(). + + + + + + + + + + Then set command, + private_data and + private_free for the private + access function, the private data and the destructor. + The l_port and r_port are not necessarily set. Only the + command must be set properly. You can retrieve the data + from the opl3->private_data field. + + + + After creating the opl3 instance via snd_opl3_new(), + call snd_opl3_init() to initialize the chip to the + proper state. Note that snd_opl3_create() always + calls it internally. + + + + If the opl3 instance is created successfully, then create a + hwdep device for this opl3. + + + + + + + + + + The first argument is the opl3_t instance you + created, and the second is the index number, usually 0. + + + + The third argument is the index-offset for the sequencer + client assigned to the OPL3 port. When there is an MPU401-UART, + give 1 for here (UART always takes 0). + +
+ +
+ Hardware-Dependent Devices + + Some chips need user-space access for special + controls or for loading the micro code. In such a case, you can + create a hwdep (hardware-dependent) device. The hwdep API is + defined in <sound/hwdep.h>. You can + find examples in opl3 driver or + isa/sb/sb16_csp.c. + + + + The creation of the hwdep instance is done via + snd_hwdep_new(). + + + + + + + + where the third argument is the index number. + + + + You can then pass any pointer value to the + private_data. + If you assign a private data, you should define the + destructor, too. The destructor function is set in + the private_free field. + + + +private_data = p; + hw->private_free = mydata_free; +]]> + + + + and the implementation of the destructor would be: + + + +private_data; + kfree(p); + } +]]> + + + + + + The arbitrary file operations can be defined for this + instance. The file operators are defined in + the ops table. For example, assume that + this chip needs an ioctl. + + + +ops.open = mydata_open; + hw->ops.ioctl = mydata_ioctl; + hw->ops.release = mydata_release; +]]> + + + + And implement the callback functions as you like. + +
+ +
+ IEC958 (S/PDIF) + + Usually the controls for IEC958 devices are implemented via + the control interface. There is a macro to compose a name string for + IEC958 controls, SNDRV_CTL_NAME_IEC958() + defined in <include/asound.h>. + + + + There are some standard controls for IEC958 status bits. These + controls use the type SNDRV_CTL_ELEM_TYPE_IEC958, + and the size of element is fixed as 4 bytes array + (value.iec958.status[x]). For the info + callback, you don't specify + the value field for this type (the count field must be set, + though). + + + + IEC958 Playback Con Mask is used to return the + bit-mask for the IEC958 status bits of consumer mode. Similarly, + IEC958 Playback Pro Mask returns the bitmask for + professional mode. They are read-only controls, and are defined + as MIXER controls (iface = + SNDRV_CTL_ELEM_IFACE_MIXER). + + + + Meanwhile, IEC958 Playback Default control is + defined for getting and setting the current default IEC958 + bits. Note that this one is usually defined as a PCM control + (iface = SNDRV_CTL_ELEM_IFACE_PCM), + although in some places it's defined as a MIXER control. + + + + In addition, you can define the control switches to + enable/disable or to set the raw bit mode. The implementation + will depend on the chip, but the control should be named as + IEC958 xxx, preferably using + the SNDRV_CTL_NAME_IEC958() macro. + + + + You can find several cases, for example, + pci/emu10k1, + pci/ice1712, or + pci/cmipci.c. + +
+ +
+ + + + + + + Buffer and Memory Management + +
+ Buffer Types + + ALSA provides several different buffer allocation functions + depending on the bus and the architecture. All these have a + consistent API. The allocation of physically-contiguous pages is + done via + snd_malloc_xxx_pages() function, where xxx + is the bus type. + + + + The allocation of pages with fallback is + snd_malloc_xxx_pages_fallback(). This + function tries to allocate the specified pages but if the pages + are not available, it tries to reduce the page sizes until + enough space is found. + + + + The release the pages, call + snd_free_xxx_pages() function. + + + + Usually, ALSA drivers try to allocate and reserve + a large contiguous physical space + at the time the module is loaded for the later use. + This is called pre-allocation. + As already written, you can call the following function at + pcm instance construction time (in the case of PCI bus). + + + + + + + + where size is the byte size to be + pre-allocated and the max is the maximum + size to be changed via the prealloc proc file. + The allocator will try to get an area as large as possible + within the given size. + + + + The second argument (type) and the third argument (device pointer) + are dependent on the bus. + In the case of the ISA bus, pass snd_dma_isa_data() + as the third argument with SNDRV_DMA_TYPE_DEV type. + For the continuous buffer unrelated to the bus can be pre-allocated + with SNDRV_DMA_TYPE_CONTINUOUS type and the + snd_dma_continuous_data(GFP_KERNEL) device pointer, + where GFP_KERNEL is the kernel allocation flag to + use. + For the PCI scatter-gather buffers, use + SNDRV_DMA_TYPE_DEV_SG with + snd_dma_pci_data(pci) + (see the + Non-Contiguous Buffers + section). + + + + Once the buffer is pre-allocated, you can use the + allocator in the hw_params callback: + + + + + + + + Note that you have to pre-allocate to use this function. + +
+ +
+ External Hardware Buffers + + Some chips have their own hardware buffers and the DMA + transfer from the host memory is not available. In such a case, + you need to either 1) copy/set the audio data directly to the + external hardware buffer, or 2) make an intermediate buffer and + copy/set the data from it to the external hardware buffer in + interrupts (or in tasklets, preferably). + + + + The first case works fine if the external hardware buffer is large + enough. This method doesn't need any extra buffers and thus is + more effective. You need to define the + copy and + silence callbacks for + the data transfer. However, there is a drawback: it cannot + be mmapped. The examples are GUS's GF1 PCM or emu8000's + wavetable PCM. + + + + The second case allows for mmap on the buffer, although you have + to handle an interrupt or a tasklet to transfer the data + from the intermediate buffer to the hardware buffer. You can find an + example in the vxpocket driver. + + + + Another case is when the chip uses a PCI memory-map + region for the buffer instead of the host memory. In this case, + mmap is available only on certain architectures like the Intel one. + In non-mmap mode, the data cannot be transferred as in the normal + way. Thus you need to define the copy and + silence callbacks as well, + as in the cases above. The examples are found in + rme32.c and rme96.c. + + + + The implementation of the copy and + silence callbacks depends upon + whether the hardware supports interleaved or non-interleaved + samples. The copy callback is + defined like below, a bit + differently depending whether the direction is playback or + capture: + + + + + + + + + + In the case of interleaved samples, the second argument + (channel) is not used. The third argument + (pos) points the + current position offset in frames. + + + + The meaning of the fourth argument is different between + playback and capture. For playback, it holds the source data + pointer, and for capture, it's the destination data pointer. + + + + The last argument is the number of frames to be copied. + + + + What you have to do in this callback is again different + between playback and capture directions. In the + playback case, you copy the given amount of data + (count) at the specified pointer + (src) to the specified offset + (pos) on the hardware buffer. When + coded like memcpy-like way, the copy would be like: + + + + + + + + + + For the capture direction, you copy the given amount of + data (count) at the specified offset + (pos) on the hardware buffer to the + specified pointer (dst). + + + + + + + + Note that both the position and the amount of data are given + in frames. + + + + In the case of non-interleaved samples, the implementation + will be a bit more complicated. + + + + You need to check the channel argument, and if it's -1, copy + the whole channels. Otherwise, you have to copy only the + specified channel. Please check + isa/gus/gus_pcm.c as an example. + + + + The silence callback is also + implemented in a similar way. + + + + + + + + + + The meanings of arguments are the same as in the + copy + callback, although there is no src/dst + argument. In the case of interleaved samples, the channel + argument has no meaning, as well as on + copy callback. + + + + The role of silence callback is to + set the given amount + (count) of silence data at the + specified offset (pos) on the hardware + buffer. Suppose that the data format is signed (that is, the + silent-data is 0), and the implementation using a memset-like + function would be like: + + + + + + + + + + In the case of non-interleaved samples, again, the + implementation becomes a bit more complicated. See, for example, + isa/gus/gus_pcm.c. + +
+ +
+ Non-Contiguous Buffers + + If your hardware supports the page table as in emu10k1 or the + buffer descriptors as in via82xx, you can use the scatter-gather + (SG) DMA. ALSA provides an interface for handling SG-buffers. + The API is provided in <sound/pcm.h>. + + + + For creating the SG-buffer handler, call + snd_pcm_lib_preallocate_pages() or + snd_pcm_lib_preallocate_pages_for_all() + with SNDRV_DMA_TYPE_DEV_SG + in the PCM constructor like other PCI pre-allocator. + You need to pass snd_dma_pci_data(pci), + where pci is the struct pci_dev pointer + of the chip as well. + The struct snd_sg_buf instance is created as + substream->dma_private. You can cast + the pointer like: + + + +dma_private; +]]> + + + + + + Then call snd_pcm_lib_malloc_pages() + in the hw_params callback + as well as in the case of normal PCI buffer. + The SG-buffer handler will allocate the non-contiguous kernel + pages of the given size and map them onto the virtually contiguous + memory. The virtual pointer is addressed in runtime->dma_area. + The physical address (runtime->dma_addr) is set to zero, + because the buffer is physically non-contigous. + The physical address table is set up in sgbuf->table. + You can get the physical address at a certain offset via + snd_pcm_sgbuf_get_addr(). + + + + When a SG-handler is used, you need to set + snd_pcm_sgbuf_ops_page as + the page callback. + (See + page callback section.) + + + + To release the data, call + snd_pcm_lib_free_pages() in the + hw_free callback as usual. + +
+ +
+ Vmalloc'ed Buffers + + It's possible to use a buffer allocated via + vmalloc, for example, for an intermediate + buffer. Since the allocated pages are not contiguous, you need + to set the page callback to obtain + the physical address at every offset. + + + + The implementation of page callback + would be like this: + + + + + + /* get the physical page pointer on the given offset */ + static struct page *mychip_page(struct snd_pcm_substream *substream, + unsigned long offset) + { + void *pageptr = substream->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); + } +]]> + + + +
+ +
+ + + + + + + Proc Interface + + ALSA provides an easy interface for procfs. The proc files are + very useful for debugging. I recommend you set up proc files if + you write a driver and want to get a running status or register + dumps. The API is found in + <sound/info.h>. + + + + To create a proc file, call + snd_card_proc_new(). + + + + + + + + where the second argument specifies the name of the proc file to be + created. The above example will create a file + my-file under the card directory, + e.g. /proc/asound/card0/my-file. + + + + Like other components, the proc entry created via + snd_card_proc_new() will be registered and + released automatically in the card registration and release + functions. + + + + When the creation is successful, the function stores a new + instance in the pointer given in the third argument. + It is initialized as a text proc file for read only. To use + this proc file as a read-only text file as it is, set the read + callback with a private data via + snd_info_set_text_ops(). + + + + + + + + where the second argument (chip) is the + private data to be used in the callbacks. The third parameter + specifies the read buffer size and the fourth + (my_proc_read) is the callback function, which + is defined like + + + + + + + + + + + In the read callback, use snd_iprintf() for + output strings, which works just like normal + printf(). For example, + + + +private_data; + + snd_iprintf(buffer, "This is my chip!\n"); + snd_iprintf(buffer, "Port = %ld\n", chip->port); + } +]]> + + + + + + The file permissions can be changed afterwards. As default, it's + set as read only for all users. If you want to add write + permission for the user (root as default), do as follows: + + + +mode = S_IFREG | S_IRUGO | S_IWUSR; +]]> + + + + and set the write buffer size and the callback + + + +c.text.write = my_proc_write; +]]> + + + + + + For the write callback, you can use + snd_info_get_line() to get a text line, and + snd_info_get_str() to retrieve a string from + the line. Some examples are found in + core/oss/mixer_oss.c, core/oss/and + pcm_oss.c. + + + + For a raw-data proc-file, set the attributes as follows: + + + +content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip; + entry->c.ops = &my_file_io_ops; + entry->size = 4096; + entry->mode = S_IFREG | S_IRUGO; +]]> + + + + + + The callback is much more complicated than the text-file + version. You need to use a low-level I/O functions such as + copy_from/to_user() to transfer the + data. + + + + local_max_size) + size = local_max_size - pos; + if (copy_to_user(buf, local_data + pos, size)) + return -EFAULT; + return size; + } +]]> + + + + + + + + + + + + Power Management + + If the chip is supposed to work with suspend/resume + functions, you need to add power-management code to the + driver. The additional code for power-management should be + ifdef'ed with + CONFIG_PM. + + + + If the driver fully supports suspend/resume + that is, the device can be + properly resumed to its state when suspend was called, + you can set the SNDRV_PCM_INFO_RESUME flag + in the pcm info field. Usually, this is possible when the + registers of the chip can be safely saved and restored to + RAM. If this is set, the trigger callback is called with + SNDRV_PCM_TRIGGER_RESUME after the resume + callback completes. + + + + Even if the driver doesn't support PM fully but + partial suspend/resume is still possible, it's still worthy to + implement suspend/resume callbacks. In such a case, applications + would reset the status by calling + snd_pcm_prepare() and restart the stream + appropriately. Hence, you can define suspend/resume callbacks + below but don't set SNDRV_PCM_INFO_RESUME + info flag to the PCM. + + + + Note that the trigger with SUSPEND can always be called when + snd_pcm_suspend_all is called, + regardless of the SNDRV_PCM_INFO_RESUME flag. + The RESUME flag affects only the behavior + of snd_pcm_resume(). + (Thus, in theory, + SNDRV_PCM_TRIGGER_RESUME isn't needed + to be handled in the trigger callback when no + SNDRV_PCM_INFO_RESUME flag is set. But, + it's better to keep it for compatibility reasons.) + + + In the earlier version of ALSA drivers, a common + power-management layer was provided, but it has been removed. + The driver needs to define the suspend/resume hooks according to + the bus the device is connected to. In the case of PCI drivers, the + callbacks look like below: + + + + + + + + + + The scheme of the real suspend job is as follows. + + + Retrieve the card and the chip data. + Call snd_power_change_state() with + SNDRV_CTL_POWER_D3hot to change the + power status. + Call snd_pcm_suspend_all() to suspend the running PCM streams. + If AC97 codecs are used, call + snd_ac97_suspend() for each codec. + Save the register values if necessary. + Stop the hardware if necessary. + Disable the PCI device by calling + pci_disable_device(). Then, call + pci_save_state() at last. + + + + + A typical code would be like: + + + +private_data; + /* (2) */ + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + /* (3) */ + snd_pcm_suspend_all(chip->pcm); + /* (4) */ + snd_ac97_suspend(chip->ac97); + /* (5) */ + snd_mychip_save_registers(chip); + /* (6) */ + snd_mychip_stop_hardware(chip); + /* (7) */ + pci_disable_device(pci); + pci_save_state(pci); + return 0; + } +]]> + + + + + + The scheme of the real resume job is as follows. + + + Retrieve the card and the chip data. + Set up PCI. First, call pci_restore_state(). + Then enable the pci device again by calling pci_enable_device(). + Call pci_set_master() if necessary, too. + Re-initialize the chip. + Restore the saved registers if necessary. + Resume the mixer, e.g. calling + snd_ac97_resume(). + Restart the hardware (if any). + Call snd_power_change_state() with + SNDRV_CTL_POWER_D0 to notify the processes. + + + + + A typical code would be like: + + + +private_data; + /* (2) */ + pci_restore_state(pci); + pci_enable_device(pci); + pci_set_master(pci); + /* (3) */ + snd_mychip_reinit_chip(chip); + /* (4) */ + snd_mychip_restore_registers(chip); + /* (5) */ + snd_ac97_resume(chip->ac97); + /* (6) */ + snd_mychip_restart_chip(chip); + /* (7) */ + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; + } +]]> + + + + + + As shown in the above, it's better to save registers after + suspending the PCM operations via + snd_pcm_suspend_all() or + snd_pcm_suspend(). It means that the PCM + streams are already stoppped when the register snapshot is + taken. But, remember that you don't have to restart the PCM + stream in the resume callback. It'll be restarted via + trigger call with SNDRV_PCM_TRIGGER_RESUME + when necessary. + + + + OK, we have all callbacks now. Let's set them up. In the + initialization of the card, make sure that you can get the chip + data from the card instance, typically via + private_data field, in case you + created the chip data individually. + + + +private_data = chip; + .... + } +]]> + + + + When you created the chip data with + snd_card_create(), it's anyway accessible + via private_data field. + + + +private_data; + .... + } +]]> + + + + + + + If you need a space to save the registers, allocate the + buffer for it here, too, since it would be fatal + if you cannot allocate a memory in the suspend phase. + The allocated buffer should be released in the corresponding + destructor. + + + + And next, set suspend/resume callbacks to the pci_driver. + + + + + + + + + + + + + + + + Module Parameters + + There are standard module options for ALSA. At least, each + module should have the index, + id and enable + options. + + + + If the module supports multiple cards (usually up to + 8 = SNDRV_CARDS cards), they should be + arrays. The default initial values are defined already as + constants for easier programming: + + + + + + + + + + If the module supports only a single card, they could be single + variables, instead. enable option is not + always necessary in this case, but it would be better to have a + dummy option for compatibility. + + + + The module parameters must be declared with the standard + module_param()(), + module_param_array()() and + MODULE_PARM_DESC() macros. + + + + The typical coding would be like below: + + + + + + + + + + Also, don't forget to define the module description, classes, + license and devices. Especially, the recent modprobe requires to + define the module license as GPL, etc., otherwise the system is + shown as tainted. + + + + + + + + + + + + + + + + How To Put Your Driver Into ALSA Tree +
+ General + + So far, you've learned how to write the driver codes. + And you might have a question now: how to put my own + driver into the ALSA driver tree? + Here (finally :) the standard procedure is described briefly. + + + + Suppose that you create a new PCI driver for the card + xyz. The card module name would be + snd-xyz. The new driver is usually put into the alsa-driver + tree, alsa-driver/pci directory in + the case of PCI cards. + Then the driver is evaluated, audited and tested + by developers and users. After a certain time, the driver + will go to the alsa-kernel tree (to the corresponding directory, + such as alsa-kernel/pci) and eventually + will be integrated into the Linux 2.6 tree (the directory would be + linux/sound/pci). + + + + In the following sections, the driver code is supposed + to be put into alsa-driver tree. The two cases are covered: + a driver consisting of a single source file and one consisting + of several source files. + +
+ +
+ Driver with A Single Source File + + + + + Modify alsa-driver/pci/Makefile + + + + Suppose you have a file xyz.c. Add the following + two lines + + + + + + + + + + + Create the Kconfig entry + + + + Add the new entry of Kconfig for your xyz driver. + + + + + + + the line, select SND_PCM, specifies that the driver xyz supports + PCM. In addition to SND_PCM, the following components are + supported for select command: + SND_RAWMIDI, SND_TIMER, SND_HWDEP, SND_MPU401_UART, + SND_OPL3_LIB, SND_OPL4_LIB, SND_VX_LIB, SND_AC97_CODEC. + Add the select command for each supported component. + + + + Note that some selections imply the lowlevel selections. + For example, PCM includes TIMER, MPU401_UART includes RAWMIDI, + AC97_CODEC includes PCM, and OPL3_LIB includes HWDEP. + You don't need to give the lowlevel selections again. + + + + For the details of Kconfig script, refer to the kbuild + documentation. + + + + + + + Run cvscompile script to re-generate the configure script and + build the whole stuff again. + + + + +
+ +
+ Drivers with Several Source Files + + Suppose that the driver snd-xyz have several source files. + They are located in the new subdirectory, + pci/xyz. + + + + + Add a new directory (xyz) in + alsa-driver/pci/Makefile as below + + + + + + + + + + + + Under the directory xyz, create a Makefile + + + Sample Makefile for a driver xyz + + + + + + + + + + Create the Kconfig entry + + + + This procedure is as same as in the last section. + + + + + + Run cvscompile script to re-generate the configure script and + build the whole stuff again. + + + + +
+ +
+ + + + + + Useful Functions + +
+ <function>snd_printk()</function> and friends + + ALSA provides a verbose version of the + printk() function. If a kernel config + CONFIG_SND_VERBOSE_PRINTK is set, this + function prints the given message together with the file name + and the line of the caller. The KERN_XXX + prefix is processed as + well as the original printk() does, so it's + recommended to add this prefix, e.g. + + + + + + + + + + There are also printk()'s for + debugging. snd_printd() can be used for + general debugging purposes. If + CONFIG_SND_DEBUG is set, this function is + compiled, and works just like + snd_printk(). If the ALSA is compiled + without the debugging flag, it's ignored. + + + + snd_printdd() is compiled in only when + CONFIG_SND_DEBUG_VERBOSE is set. Please note + that CONFIG_SND_DEBUG_VERBOSE is not set as default + even if you configure the alsa-driver with + option. You need to give + explicitly option instead. + +
+ +
+ <function>snd_BUG()</function> + + It shows the BUG? message and + stack trace as well as snd_BUG_ON at the point. + It's useful to show that a fatal error happens there. + + + When no debug flag is set, this macro is ignored. + +
+ +
+ <function>snd_BUG_ON()</function> + + snd_BUG_ON() macro is similar with + WARN_ON() macro. For example, + + + + + + + + or it can be used as the condition, + + + + + + + + + + The macro takes an conditional expression to evaluate. + When CONFIG_SND_DEBUG, is set, the + expression is actually evaluated. If it's non-zero, it shows + the warning message such as + BUG? (xxx) + normally followed by stack trace. It returns the evaluated + value. + When no CONFIG_SND_DEBUG is set, this + macro always returns zero. + + +
+ +
+ + + + + + + Acknowledgments + + I would like to thank Phil Kerr for his help for improvement and + corrections of this document. + + + Kevin Conder reformatted the original plain-text to the + DocBook format. + + + Giuliano Pochini corrected typos and contributed the example codes + in the hardware constraints section. + + +
diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl deleted file mode 100644 index 0230a96f0564..000000000000 --- a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - The ALSA Driver API - - - - This document is free; 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 document is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the - implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, - MA 02111-1307 USA - - - - - - - - Management of Cards and Devices - Card Management -!Esound/core/init.c - - Device Components -!Esound/core/device.c - - Module requests and Device File Entries -!Esound/core/sound.c - - Memory Management Helpers -!Esound/core/memory.c -!Esound/core/memalloc.c - - - PCM API - PCM Core -!Esound/core/pcm.c -!Esound/core/pcm_lib.c -!Esound/core/pcm_native.c - - PCM Format Helpers -!Esound/core/pcm_misc.c - - PCM Memory Management -!Esound/core/pcm_memory.c - - - Control/Mixer API - General Control Interface -!Esound/core/control.c - - AC97 Codec API -!Esound/pci/ac97/ac97_codec.c -!Esound/pci/ac97/ac97_pcm.c - - Virtual Master Control API -!Esound/core/vmaster.c -!Iinclude/sound/control.h - - - MIDI API - Raw MIDI API -!Esound/core/rawmidi.c - - MPU401-UART API -!Esound/drivers/mpu401/mpu401_uart.c - - - Proc Info API - Proc Info Interface -!Esound/core/info.c - - - Miscellaneous Functions - Hardware-Dependent Devices API -!Esound/core/hwdep.c - - Jack Abstraction Layer API -!Esound/core/jack.c - - ISA DMA Helpers -!Esound/core/isadma.c - - Other Helper Macros -!Iinclude/sound/core.h - - - - diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl deleted file mode 100644 index 46b08fef3744..000000000000 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ /dev/null @@ -1,6216 +0,0 @@ - - - - - - - - - Writing an ALSA Driver - - Takashi - Iwai - -
- tiwai@suse.de -
-
-
- - Oct 15, 2007 - 0.3.7 - - - - This document describes how to write an ALSA (Advanced Linux - Sound Architecture) driver. - - - - - - Copyright (c) 2002-2005 Takashi Iwai tiwai@suse.de - - - - This document is free; 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 document is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the - implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, - MA 02111-1307 USA - - - -
- - - - - - Preface - - This document describes how to write an - - ALSA (Advanced Linux Sound Architecture) - driver. The document focuses mainly on PCI soundcards. - In the case of other device types, the API might - be different, too. However, at least the ALSA kernel API is - consistent, and therefore it would be still a bit help for - writing them. - - - - This document targets people who already have enough - C language skills and have basic linux kernel programming - knowledge. This document doesn't explain the general - topic of linux kernel coding and doesn't cover low-level - driver implementation details. It only describes - the standard way to write a PCI sound driver on ALSA. - - - - If you are already familiar with the older ALSA ver.0.5.x API, you - can check the drivers such as sound/pci/es1938.c or - sound/pci/maestro3.c which have also almost the same - code-base in the ALSA 0.5.x tree, so you can compare the differences. - - - - This document is still a draft version. Any feedback and - corrections, please!! - - - - - - - - - File Tree Structure - -
- General - - The ALSA drivers are provided in two ways. - - - - One is the trees provided as a tarball or via cvs from the - ALSA's ftp site, and another is the 2.6 (or later) Linux kernel - tree. To synchronize both, the ALSA driver tree is split into - two different trees: alsa-kernel and alsa-driver. The former - contains purely the source code for the Linux 2.6 (or later) - tree. This tree is designed only for compilation on 2.6 or - later environment. The latter, alsa-driver, contains many subtle - files for compiling ALSA drivers outside of the Linux kernel tree, - wrapper functions for older 2.2 and 2.4 kernels, to adapt the latest kernel API, - and additional drivers which are still in development or in - tests. The drivers in alsa-driver tree will be moved to - alsa-kernel (and eventually to the 2.6 kernel tree) when they are - finished and confirmed to work fine. - - - - The file tree structure of ALSA driver is depicted below. Both - alsa-kernel and alsa-driver have almost the same file - structure, except for core directory. It's - named as acore in alsa-driver tree. - - - ALSA File Tree Structure - - sound - /core - /oss - /seq - /oss - /instr - /ioctl32 - /include - /drivers - /mpu401 - /opl3 - /i2c - /l3 - /synth - /emux - /pci - /(cards) - /isa - /(cards) - /arm - /ppc - /sparc - /usb - /pcmcia /(cards) - /oss - - - -
- -
- core directory - - This directory contains the middle layer which is the heart - of ALSA drivers. In this directory, the native ALSA modules are - stored. The sub-directories contain different modules and are - dependent upon the kernel config. - - -
- core/oss - - - The codes for PCM and mixer OSS emulation modules are stored - in this directory. The rawmidi OSS emulation is included in - the ALSA rawmidi code since it's quite small. The sequencer - code is stored in core/seq/oss directory (see - - below). - -
- -
- core/ioctl32 - - - This directory contains the 32bit-ioctl wrappers for 64bit - architectures such like x86-64, ppc64 and sparc64. For 32bit - and alpha architectures, these are not compiled. - -
- -
- core/seq - - This directory and its sub-directories are for the ALSA - sequencer. This directory contains the sequencer core and - primary sequencer modules such like snd-seq-midi, - snd-seq-virmidi, etc. They are compiled only when - CONFIG_SND_SEQUENCER is set in the kernel - config. - -
- -
- core/seq/oss - - This contains the OSS sequencer emulation codes. - -
- -
- core/seq/instr - - This directory contains the modules for the sequencer - instrument layer. - -
-
- -
- include directory - - This is the place for the public header files of ALSA drivers, - which are to be exported to user-space, or included by - several files at different directories. Basically, the private - header files should not be placed in this directory, but you may - still find files there, due to historical reasons :) - -
- -
- drivers directory - - This directory contains code shared among different drivers - on different architectures. They are hence supposed not to be - architecture-specific. - For example, the dummy pcm driver and the serial MIDI - driver are found in this directory. In the sub-directories, - there is code for components which are independent from - bus and cpu architectures. - - -
- drivers/mpu401 - - The MPU401 and MPU401-UART modules are stored here. - -
- -
- drivers/opl3 and opl4 - - The OPL3 and OPL4 FM-synth stuff is found here. - -
-
- -
- i2c directory - - This contains the ALSA i2c components. - - - - Although there is a standard i2c layer on Linux, ALSA has its - own i2c code for some cards, because the soundcard needs only a - simple operation and the standard i2c API is too complicated for - such a purpose. - - -
- i2c/l3 - - This is a sub-directory for ARM L3 i2c. - -
-
- -
- synth directory - - This contains the synth middle-level modules. - - - - So far, there is only Emu8000/Emu10k1 synth driver under - the synth/emux sub-directory. - -
- -
- pci directory - - This directory and its sub-directories hold the top-level card modules - for PCI soundcards and the code specific to the PCI BUS. - - - - The drivers compiled from a single file are stored directly - in the pci directory, while the drivers with several source files are - stored on their own sub-directory (e.g. emu10k1, ice1712). - -
- -
- isa directory - - This directory and its sub-directories hold the top-level card modules - for ISA soundcards. - -
- -
- arm, ppc, and sparc directories - - They are used for top-level card modules which are - specific to one of these architectures. - -
- -
- usb directory - - This directory contains the USB-audio driver. In the latest version, the - USB MIDI driver is integrated in the usb-audio driver. - -
- -
- pcmcia directory - - The PCMCIA, especially PCCard drivers will go here. CardBus - drivers will be in the pci directory, because their API is identical - to that of standard PCI cards. - -
- -
- oss directory - - The OSS/Lite source files are stored here in Linux 2.6 (or - later) tree. In the ALSA driver tarball, this directory is empty, - of course :) - -
-
- - - - - - - Basic Flow for PCI Drivers - -
- Outline - - The minimum flow for PCI soundcards is as follows: - - - define the PCI ID table (see the section - PCI Entries - ). - create probe() callback. - create remove() callback. - create a pci_driver structure - containing the three pointers above. - create an init() function just calling - the pci_register_driver() to register the pci_driver table - defined above. - create an exit() function to call - the pci_unregister_driver() function. - - -
- -
- Full Code Example - - The code example is shown below. Some parts are kept - unimplemented at this moment but will be filled in the - next sections. The numbers in the comment lines of the - snd_mychip_probe() function - refer to details explained in the following section. - - - Basic Flow for PCI Drivers - Example - - - #include - #include - #include - #include - - /* module parameters (see "Module Parameters") */ - /* SNDRV_CARDS: maximum number of cards supported by this module */ - static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; - static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; - static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; - - /* definition of the chip-specific record */ - struct mychip { - struct snd_card *card; - /* the rest of the implementation will be in section - * "PCI Resource Management" - */ - }; - - /* chip-specific destructor - * (see "PCI Resource Management") - */ - static int snd_mychip_free(struct mychip *chip) - { - .... /* will be implemented later... */ - } - - /* component-destructor - * (see "Management of Cards and Components") - */ - static int snd_mychip_dev_free(struct snd_device *device) - { - return snd_mychip_free(device->device_data); - } - - /* chip-specific constructor - * (see "Management of Cards and Components") - */ - static int __devinit snd_mychip_create(struct snd_card *card, - struct pci_dev *pci, - struct mychip **rchip) - { - struct mychip *chip; - int err; - static struct snd_device_ops ops = { - .dev_free = snd_mychip_dev_free, - }; - - *rchip = NULL; - - /* check PCI availability here - * (see "PCI Resource Management") - */ - .... - - /* allocate a chip-specific data with zero filled */ - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - - chip->card = card; - - /* rest of initialization here; will be implemented - * later, see "PCI Resource Management" - */ - .... - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); - if (err < 0) { - snd_mychip_free(chip); - return err; - } - - snd_card_set_dev(card, &pci->dev); - - *rchip = chip; - return 0; - } - - /* constructor -- see "Constructor" sub-section */ - static int __devinit snd_mychip_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) - { - static int dev; - struct snd_card *card; - struct mychip *chip; - int err; - - /* (1) */ - if (dev >= SNDRV_CARDS) - return -ENODEV; - if (!enable[dev]) { - dev++; - return -ENOENT; - } - - /* (2) */ - err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); - if (err < 0) - return err; - - /* (3) */ - err = snd_mychip_create(card, pci, &chip); - if (err < 0) { - snd_card_free(card); - return err; - } - - /* (4) */ - strcpy(card->driver, "My Chip"); - strcpy(card->shortname, "My Own Chip 123"); - sprintf(card->longname, "%s at 0x%lx irq %i", - card->shortname, chip->ioport, chip->irq); - - /* (5) */ - .... /* implemented later */ - - /* (6) */ - err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } - - /* (7) */ - pci_set_drvdata(pci, card); - dev++; - return 0; - } - - /* destructor -- see the "Destructor" sub-section */ - static void __devexit snd_mychip_remove(struct pci_dev *pci) - { - snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); - } -]]> - - - -
- -
- Constructor - - The real constructor of PCI drivers is the probe callback. - The probe callback and other component-constructors which are called - from the probe callback should be defined with - the __devinit prefix. You - cannot use the __init prefix for them, - because any PCI device could be a hotplug device. - - - - In the probe callback, the following scheme is often used. - - -
- 1) Check and increment the device index. - - - -= SNDRV_CARDS) - return -ENODEV; - if (!enable[dev]) { - dev++; - return -ENOENT; - } -]]> - - - - where enable[dev] is the module option. - - - - Each time the probe callback is called, check the - availability of the device. If not available, simply increment - the device index and returns. dev will be incremented also - later (step - 7). - -
- -
- 2) Create a card instance - - - - - - - - - - The details will be explained in the section - - Management of Cards and Components. - -
- -
- 3) Create a main component - - In this part, the PCI resources are allocated. - - - - - - - - The details will be explained in the section PCI Resource - Management. - -
- -
- 4) Set the driver ID and name strings. - - - -driver, "My Chip"); - strcpy(card->shortname, "My Own Chip 123"); - sprintf(card->longname, "%s at 0x%lx irq %i", - card->shortname, chip->ioport, chip->irq); -]]> - - - - The driver field holds the minimal ID string of the - chip. This is used by alsa-lib's configurator, so keep it - simple but unique. - Even the same driver can have different driver IDs to - distinguish the functionality of each chip type. - - - - The shortname field is a string shown as more verbose - name. The longname field contains the information - shown in /proc/asound/cards. - -
- -
- 5) Create other components, such as mixer, MIDI, etc. - - Here you define the basic components such as - PCM, - mixer (e.g. AC97), - MIDI (e.g. MPU-401), - and other interfaces. - Also, if you want a proc - file, define it here, too. - -
- -
- 6) Register the card instance. - - - - - - - - - - Will be explained in the section Management - of Cards and Components, too. - -
- -
- 7) Set the PCI driver data and return zero. - - - - - - - - In the above, the card record is stored. This pointer is - used in the remove callback and power-management - callbacks, too. - -
-
- -
- Destructor - - The destructor, remove callback, simply releases the card - instance. Then the ALSA middle layer will release all the - attached components automatically. - - - - It would be typically like the following: - - - - - - - - The above code assumes that the card pointer is set to the PCI - driver data. - -
- -
- Header Files - - For the above example, at least the following include files - are necessary. - - - - - #include - #include - #include - #include -]]> - - - - where the last one is necessary only when module options are - defined in the source file. If the code is split into several - files, the files without module options don't need them. - - - - In addition to these headers, you'll need - <linux/interrupt.h> for interrupt - handling, and <asm/io.h> for I/O - access. If you use the mdelay() or - udelay() functions, you'll need to include - <linux/delay.h> too. - - - - The ALSA interfaces like the PCM and control APIs are defined in other - <sound/xxx.h> header files. - They have to be included after - <sound/core.h>. - - -
-
- - - - - - - Management of Cards and Components - -
- Card Instance - - For each soundcard, a card record must be allocated. - - - - A card record is the headquarters of the soundcard. It manages - the whole list of devices (components) on the soundcard, such as - PCM, mixers, MIDI, synthesizer, and so on. Also, the card - record holds the ID and the name strings of the card, manages - the root of proc files, and controls the power-management states - and hotplug disconnections. The component list on the card - record is used to manage the correct release of resources at - destruction. - - - - As mentioned above, to create a card instance, call - snd_card_create(). - - - - - - - - - - The function takes five arguments, the card-index number, the - id string, the module pointer (usually - THIS_MODULE), - the size of extra-data space, and the pointer to return the - card instance. The extra_size argument is used to - allocate card->private_data for the - chip-specific data. Note that these data - are allocated by snd_card_create(). - -
- -
- Components - - After the card is created, you can attach the components - (devices) to the card instance. In an ALSA driver, a component is - represented as a struct snd_device object. - A component can be a PCM instance, a control interface, a raw - MIDI interface, etc. Each such instance has one component - entry. - - - - A component can be created via - snd_device_new() function. - - - - - - - - - - This takes the card pointer, the device-level - (SNDRV_DEV_XXX), the data pointer, and the - callback pointers (&ops). The - device-level defines the type of components and the order of - registration and de-registration. For most components, the - device-level is already defined. For a user-defined component, - you can use SNDRV_DEV_LOWLEVEL. - - - - This function itself doesn't allocate the data space. The data - must be allocated manually beforehand, and its pointer is passed - as the argument. This pointer is used as the - (chip identifier in the above example) - for the instance. - - - - Each pre-defined ALSA component such as ac97 and pcm calls - snd_device_new() inside its - constructor. The destructor for each component is defined in the - callback pointers. Hence, you don't need to take care of - calling a destructor for such a component. - - - - If you wish to create your own component, you need to - set the destructor function to the dev_free callback in - the ops, so that it can be released - automatically via snd_card_free(). - The next example will show an implementation of chip-specific - data. - -
- -
- Chip-Specific Data - - Chip-specific information, e.g. the I/O port address, its - resource pointer, or the irq number, is stored in the - chip-specific record. - - - - - - - - - - In general, there are two ways of allocating the chip record. - - -
- 1. Allocating via <function>snd_card_create()</function>. - - As mentioned above, you can pass the extra-data-length - to the 4th argument of snd_card_create(), i.e. - - - - - - - - struct mychip is the type of the chip record. - - - - In return, the allocated record can be accessed as - - - -private_data; -]]> - - - - With this method, you don't have to allocate twice. - The record is released together with the card instance. - -
- -
- 2. Allocating an extra device. - - - After allocating a card instance via - snd_card_create() (with - 0 on the 4th arg), call - kzalloc(). - - - - - - - - - - The chip record should have the field to hold the card - pointer at least, - - - - - - - - - - Then, set the card pointer in the returned chip instance. - - - -card = card; -]]> - - - - - - Next, initialize the fields, and register this chip - record as a low-level device with a specified - ops, - - - - - - - - snd_mychip_dev_free() is the - device-destructor function, which will call the real - destructor. - - - - - -device_data); - } -]]> - - - - where snd_mychip_free() is the real destructor. - -
-
- -
- Registration and Release - - After all components are assigned, register the card instance - by calling snd_card_register(). Access - to the device files is enabled at this point. That is, before - snd_card_register() is called, the - components are safely inaccessible from external side. If this - call fails, exit the probe function after releasing the card via - snd_card_free(). - - - - For releasing the card instance, you can call simply - snd_card_free(). As mentioned earlier, all - components are released automatically by this call. - - - - As further notes, the destructors (both - snd_mychip_dev_free and - snd_mychip_free) cannot be defined with - the __devexit prefix, because they may be - called from the constructor, too, at the false path. - - - - For a device which allows hotplugging, you can use - snd_card_free_when_closed. This one will - postpone the destruction until all devices are closed. - - -
- -
- - - - - - - PCI Resource Management - -
- Full Code Example - - In this section, we'll complete the chip-specific constructor, - destructor and PCI entries. Example code is shown first, - below. - - - PCI Resource Management Example - -irq >= 0) - free_irq(chip->irq, chip); - /* release the I/O ports & memory */ - pci_release_regions(chip->pci); - /* disable the PCI entry */ - pci_disable_device(chip->pci); - /* release the data */ - kfree(chip); - return 0; - } - - /* chip-specific constructor */ - static int __devinit snd_mychip_create(struct snd_card *card, - struct pci_dev *pci, - struct mychip **rchip) - { - struct mychip *chip; - int err; - static struct snd_device_ops ops = { - .dev_free = snd_mychip_dev_free, - }; - - *rchip = NULL; - - /* initialize the PCI entry */ - err = pci_enable_device(pci); - if (err < 0) - return err; - /* check PCI availability (28bit DMA) */ - if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 || - pci_set_consistent_dma_mask(pci, DMA_28BIT_MASK) < 0) { - printk(KERN_ERR "error to set 28bit mask DMA\n"); - pci_disable_device(pci); - return -ENXIO; - } - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (chip == NULL) { - pci_disable_device(pci); - return -ENOMEM; - } - - /* initialize the stuff */ - chip->card = card; - chip->pci = pci; - chip->irq = -1; - - /* (1) PCI resource allocation */ - err = pci_request_regions(pci, "My Chip"); - if (err < 0) { - kfree(chip); - pci_disable_device(pci); - return err; - } - chip->port = pci_resource_start(pci, 0); - if (request_irq(pci->irq, snd_mychip_interrupt, - IRQF_SHARED, "My Chip", chip)) { - printk(KERN_ERR "cannot grab irq %d\n", pci->irq); - snd_mychip_free(chip); - return -EBUSY; - } - chip->irq = pci->irq; - - /* (2) initialization of the chip hardware */ - .... /* (not implemented in this document) */ - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); - if (err < 0) { - snd_mychip_free(chip); - return err; - } - - snd_card_set_dev(card, &pci->dev); - - *rchip = chip; - return 0; - } - - /* PCI IDs */ - static struct pci_device_id snd_mychip_ids[] = { - { PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_BAR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - .... - { 0, } - }; - MODULE_DEVICE_TABLE(pci, snd_mychip_ids); - - /* pci_driver definition */ - static struct pci_driver driver = { - .name = "My Own Chip", - .id_table = snd_mychip_ids, - .probe = snd_mychip_probe, - .remove = __devexit_p(snd_mychip_remove), - }; - - /* module initialization */ - static int __init alsa_card_mychip_init(void) - { - return pci_register_driver(&driver); - } - - /* module clean up */ - static void __exit alsa_card_mychip_exit(void) - { - pci_unregister_driver(&driver); - } - - module_init(alsa_card_mychip_init) - module_exit(alsa_card_mychip_exit) - - EXPORT_NO_SYMBOLS; /* for old kernels only */ -]]> - - - -
- -
- Some Hafta's - - The allocation of PCI resources is done in the - probe() function, and usually an extra - xxx_create() function is written for this - purpose. - - - - In the case of PCI devices, you first have to call - the pci_enable_device() function before - allocating resources. Also, you need to set the proper PCI DMA - mask to limit the accessed I/O range. In some cases, you might - need to call pci_set_master() function, - too. - - - - Suppose the 28bit mask, and the code to be added would be like: - - - - - - - -
- -
- Resource Allocation - - The allocation of I/O ports and irqs is done via standard kernel - functions. Unlike ALSA ver.0.5.x., there are no helpers for - that. And these resources must be released in the destructor - function (see below). Also, on ALSA 0.9.x, you don't need to - allocate (pseudo-)DMA for PCI like in ALSA 0.5.x. - - - - Now assume that the PCI device has an I/O port with 8 bytes - and an interrupt. Then struct mychip will have the - following fields: - - - - - - - - - - For an I/O port (and also a memory region), you need to have - the resource pointer for the standard resource management. For - an irq, you have to keep only the irq number (integer). But you - need to initialize this number as -1 before actual allocation, - since irq 0 is valid. The port address and its resource pointer - can be initialized as null by - kzalloc() automatically, so you - don't have to take care of resetting them. - - - - The allocation of an I/O port is done like this: - - - -port = pci_resource_start(pci, 0); -]]> - - - - - - - It will reserve the I/O port region of 8 bytes of the given - PCI device. The returned value, chip->res_port, is allocated - via kmalloc() by - request_region(). The pointer must be - released via kfree(), but there is a - problem with this. This issue will be explained later. - - - - The allocation of an interrupt source is done like this: - - - -irq, snd_mychip_interrupt, - IRQF_SHARED, "My Chip", chip)) { - printk(KERN_ERR "cannot grab irq %d\n", pci->irq); - snd_mychip_free(chip); - return -EBUSY; - } - chip->irq = pci->irq; -]]> - - - - where snd_mychip_interrupt() is the - interrupt handler defined later. - Note that chip->irq should be defined - only when request_irq() succeeded. - - - - On the PCI bus, interrupts can be shared. Thus, - IRQF_SHARED is used as the interrupt flag of - request_irq(). - - - - The last argument of request_irq() is the - data pointer passed to the interrupt handler. Usually, the - chip-specific record is used for that, but you can use what you - like, too. - - - - I won't give details about the interrupt handler at this - point, but at least its appearance can be explained now. The - interrupt handler looks usually like the following: - - - - - - - - - - Now let's write the corresponding destructor for the resources - above. The role of destructor is simple: disable the hardware - (if already activated) and release the resources. So far, we - have no hardware part, so the disabling code is not written here. - - - - To release the resources, the check-and-release - method is a safer way. For the interrupt, do like this: - - - -irq >= 0) - free_irq(chip->irq, chip); -]]> - - - - Since the irq number can start from 0, you should initialize - chip->irq with a negative value (e.g. -1), so that you can - check the validity of the irq number as above. - - - - When you requested I/O ports or memory regions via - pci_request_region() or - pci_request_regions() like in this example, - release the resource(s) using the corresponding function, - pci_release_region() or - pci_release_regions(). - - - -pci); -]]> - - - - - - When you requested manually via request_region() - or request_mem_region, you can release it via - release_resource(). Suppose that you keep - the resource pointer returned from request_region() - in chip->res_port, the release procedure looks like: - - - -res_port); -]]> - - - - - - Don't forget to call pci_disable_device() - before the end. - - - - And finally, release the chip-specific record. - - - - - - - - - - Again, remember that you cannot - use the __devexit prefix for this destructor. - - - - We didn't implement the hardware disabling part in the above. - If you need to do this, please note that the destructor may be - called even before the initialization of the chip is completed. - It would be better to have a flag to skip hardware disabling - if the hardware was not initialized yet. - - - - When the chip-data is assigned to the card using - snd_device_new() with - SNDRV_DEV_LOWLELVEL , its destructor is - called at the last. That is, it is assured that all other - components like PCMs and controls have already been released. - You don't have to stop PCMs, etc. explicitly, but just - call low-level hardware stopping. - - - - The management of a memory-mapped region is almost as same as - the management of an I/O port. You'll need three fields like - the following: - - - - - - - - and the allocation would be like below: - - - -iobase_phys = pci_resource_start(pci, 0); - chip->iobase_virt = ioremap_nocache(chip->iobase_phys, - pci_resource_len(pci, 0)); -]]> - - - - and the corresponding destructor would be: - - - -iobase_virt) - iounmap(chip->iobase_virt); - .... - pci_release_regions(chip->pci); - .... - } -]]> - - - - -
- -
- Registration of Device Struct - - At some point, typically after calling snd_device_new(), - you need to register the struct device of the chip - you're handling for udev and co. ALSA provides a macro for compatibility with - older kernels. Simply call like the following: - - -dev); -]]> - - - so that it stores the PCI's device pointer to the card. This will be - referred by ALSA core functions later when the devices are registered. - - - In the case of non-PCI, pass the proper device struct pointer of the BUS - instead. (In the case of legacy ISA without PnP, you don't have to do - anything.) - -
- -
- PCI Entries - - So far, so good. Let's finish the missing PCI - stuff. At first, we need a - pci_device_id table for this - chipset. It's a table of PCI vendor/device ID number, and some - masks. - - - - For example, - - - - - - - - - - The first and second fields of - the pci_device_id structure are the vendor and - device IDs. If you have no reason to filter the matching - devices, you can leave the remaining fields as above. The last - field of the pci_device_id struct contains - private data for this entry. You can specify any value here, for - example, to define specific operations for supported device IDs. - Such an example is found in the intel8x0 driver. - - - - The last entry of this list is the terminator. You must - specify this all-zero entry. - - - - Then, prepare the pci_driver record: - - - - - - - - - - The probe and - remove functions have already - been defined in the previous sections. - The remove function should - be defined with the - __devexit_p() macro, so that it's not - defined for built-in (and non-hot-pluggable) case. The - name - field is the name string of this device. Note that you must not - use a slash / in this string. - - - - And at last, the module entries: - - - - - - - - - - Note that these module entries are tagged with - __init and - __exit prefixes, not - __devinit nor - __devexit. - - - - Oh, one thing was forgotten. If you have no exported symbols, - you need to declare it in 2.2 or 2.4 kernels (it's not necessary in 2.6 kernels). - - - - - - - - That's all! - -
-
- - - - - - - PCM Interface - -
- General - - The PCM middle layer of ALSA is quite powerful and it is only - necessary for each driver to implement the low-level functions - to access its hardware. - - - - For accessing to the PCM layer, you need to include - <sound/pcm.h> first. In addition, - <sound/pcm_params.h> might be needed - if you access to some functions related with hw_param. - - - - Each card device can have up to four pcm instances. A pcm - instance corresponds to a pcm device file. The limitation of - number of instances comes only from the available bit size of - the Linux's device numbers. Once when 64bit device number is - used, we'll have more pcm instances available. - - - - A pcm instance consists of pcm playback and capture streams, - and each pcm stream consists of one or more pcm substreams. Some - soundcards support multiple playback functions. For example, - emu10k1 has a PCM playback of 32 stereo substreams. In this case, at - each open, a free substream is (usually) automatically chosen - and opened. Meanwhile, when only one substream exists and it was - already opened, the successful open will either block - or error with EAGAIN according to the - file open mode. But you don't have to care about such details in your - driver. The PCM middle layer will take care of such work. - -
- -
- Full Code Example - - The example code below does not include any hardware access - routines but shows only the skeleton, how to build up the PCM - interfaces. - - - PCM Example Code - - - .... - - /* hardware definition */ - static struct snd_pcm_hardware snd_mychip_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_8000_48000, - .rate_min = 8000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 32768, - .period_bytes_min = 4096, - .period_bytes_max = 32768, - .periods_min = 1, - .periods_max = 1024, - }; - - /* hardware definition */ - static struct snd_pcm_hardware snd_mychip_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_8000_48000, - .rate_min = 8000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 32768, - .period_bytes_min = 4096, - .period_bytes_max = 32768, - .periods_min = 1, - .periods_max = 1024, - }; - - /* open callback */ - static int snd_mychip_playback_open(struct snd_pcm_substream *substream) - { - struct mychip *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - runtime->hw = snd_mychip_playback_hw; - /* more hardware-initialization will be done here */ - .... - return 0; - } - - /* close callback */ - static int snd_mychip_playback_close(struct snd_pcm_substream *substream) - { - struct mychip *chip = snd_pcm_substream_chip(substream); - /* the hardware-specific codes will be here */ - .... - return 0; - - } - - /* open callback */ - static int snd_mychip_capture_open(struct snd_pcm_substream *substream) - { - struct mychip *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - runtime->hw = snd_mychip_capture_hw; - /* more hardware-initialization will be done here */ - .... - return 0; - } - - /* close callback */ - static int snd_mychip_capture_close(struct snd_pcm_substream *substream) - { - struct mychip *chip = snd_pcm_substream_chip(substream); - /* the hardware-specific codes will be here */ - .... - return 0; - - } - - /* hw_params callback */ - static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) - { - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - } - - /* hw_free callback */ - static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream) - { - return snd_pcm_lib_free_pages(substream); - } - - /* prepare callback */ - static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream) - { - struct mychip *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - /* set up the hardware with the current configuration - * for example... - */ - mychip_set_sample_format(chip, runtime->format); - mychip_set_sample_rate(chip, runtime->rate); - mychip_set_channels(chip, runtime->channels); - mychip_set_dma_setup(chip, runtime->dma_addr, - chip->buffer_size, - chip->period_size); - return 0; - } - - /* trigger callback */ - static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream, - int cmd) - { - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* do something to start the PCM engine */ - .... - break; - case SNDRV_PCM_TRIGGER_STOP: - /* do something to stop the PCM engine */ - .... - break; - default: - return -EINVAL; - } - } - - /* pointer callback */ - static snd_pcm_uframes_t - snd_mychip_pcm_pointer(struct snd_pcm_substream *substream) - { - struct mychip *chip = snd_pcm_substream_chip(substream); - unsigned int current_ptr; - - /* get the current hardware pointer */ - current_ptr = mychip_get_hw_pointer(chip); - return current_ptr; - } - - /* operators */ - static struct snd_pcm_ops snd_mychip_playback_ops = { - .open = snd_mychip_playback_open, - .close = snd_mychip_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_mychip_pcm_hw_params, - .hw_free = snd_mychip_pcm_hw_free, - .prepare = snd_mychip_pcm_prepare, - .trigger = snd_mychip_pcm_trigger, - .pointer = snd_mychip_pcm_pointer, - }; - - /* operators */ - static struct snd_pcm_ops snd_mychip_capture_ops = { - .open = snd_mychip_capture_open, - .close = snd_mychip_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_mychip_pcm_hw_params, - .hw_free = snd_mychip_pcm_hw_free, - .prepare = snd_mychip_pcm_prepare, - .trigger = snd_mychip_pcm_trigger, - .pointer = snd_mychip_pcm_pointer, - }; - - /* - * definitions of capture are omitted here... - */ - - /* create a pcm device */ - static int __devinit snd_mychip_new_pcm(struct mychip *chip) - { - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm); - if (err < 0) - return err; - pcm->private_data = chip; - strcpy(pcm->name, "My Chip"); - chip->pcm = pcm; - /* set operators */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_mychip_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_mychip_capture_ops); - /* pre-allocation of buffers */ - /* NOTE: this may fail */ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - 64*1024, 64*1024); - return 0; - } -]]> - - - -
- -
- Constructor - - A pcm instance is allocated by the snd_pcm_new() - function. It would be better to create a constructor for pcm, - namely, - - - -card, "My Chip", 0, 1, 1, &pcm); - if (err < 0) - return err; - pcm->private_data = chip; - strcpy(pcm->name, "My Chip"); - chip->pcm = pcm; - .... - return 0; - } -]]> - - - - - - The snd_pcm_new() function takes four - arguments. The first argument is the card pointer to which this - pcm is assigned, and the second is the ID string. - - - - The third argument (index, 0 in the - above) is the index of this new pcm. It begins from zero. If - you create more than one pcm instances, specify the - different numbers in this argument. For example, - index = 1 for the second PCM device. - - - - The fourth and fifth arguments are the number of substreams - for playback and capture, respectively. Here 1 is used for - both arguments. When no playback or capture substreams are available, - pass 0 to the corresponding argument. - - - - If a chip supports multiple playbacks or captures, you can - specify more numbers, but they must be handled properly in - open/close, etc. callbacks. When you need to know which - substream you are referring to, then it can be obtained from - struct snd_pcm_substream data passed to each callback - as follows: - - - -number; -]]> - - - - - - After the pcm is created, you need to set operators for each - pcm stream. - - - - - - - - - - The operators are defined typically like this: - - - - - - - - All the callbacks are described in the - - Operators subsection. - - - - After setting the operators, you probably will want to - pre-allocate the buffer. For the pre-allocation, simply call - the following: - - - -pci), - 64*1024, 64*1024); -]]> - - - - It will allocate a buffer up to 64kB as default. - Buffer management details will be described in the later section Buffer and Memory - Management. - - - - Additionally, you can set some extra information for this pcm - in pcm->info_flags. - The available values are defined as - SNDRV_PCM_INFO_XXX in - <sound/asound.h>, which is used for - the hardware definition (described later). When your soundchip - supports only half-duplex, specify like this: - - - -info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; -]]> - - - -
- -
- ... And the Destructor? - - The destructor for a pcm instance is not always - necessary. Since the pcm device will be released by the middle - layer code automatically, you don't have to call the destructor - explicitly. - - - - The destructor would be necessary if you created - special records internally and needed to release them. In such a - case, set the destructor function to - pcm->private_free: - - - PCM Instance with a Destructor - -my_private_pcm_data); - /* do what you like else */ - .... - } - - static int __devinit snd_mychip_new_pcm(struct mychip *chip) - { - struct snd_pcm *pcm; - .... - /* allocate your own data */ - chip->my_private_pcm_data = kmalloc(...); - /* set the destructor */ - pcm->private_data = chip; - pcm->private_free = mychip_pcm_free; - .... - } -]]> - - - -
- -
- Runtime Pointer - The Chest of PCM Information - - When the PCM substream is opened, a PCM runtime instance is - allocated and assigned to the substream. This pointer is - accessible via substream->runtime. - This runtime pointer holds most information you need - to control the PCM: the copy of hw_params and sw_params configurations, the buffer - pointers, mmap records, spinlocks, etc. - - - - The definition of runtime instance is found in - <sound/pcm.h>. Here are - the contents of this file: - - - - - - - - - For the operators (callbacks) of each sound driver, most of - these records are supposed to be read-only. Only the PCM - middle-layer changes / updates them. The exceptions are - the hardware description (hw), interrupt callbacks - (transfer_ack_xxx), DMA buffer information, and the private - data. Besides, if you use the standard buffer allocation - method via snd_pcm_lib_malloc_pages(), - you don't need to set the DMA buffer information by yourself. - - - - In the sections below, important records are explained. - - -
- Hardware Description - - The hardware descriptor (struct snd_pcm_hardware) - contains the definitions of the fundamental hardware - configuration. Above all, you'll need to define this in - - the open callback. - Note that the runtime instance holds the copy of the - descriptor, not the pointer to the existing descriptor. That - is, in the open callback, you can modify the copied descriptor - (runtime->hw) as you need. For example, if the maximum - number of channels is 1 only on some chip models, you can - still use the same hardware descriptor and change the - channels_max later: - - -runtime; - ... - runtime->hw = snd_mychip_playback_hw; /* common definition */ - if (chip->model == VERY_OLD_ONE) - runtime->hw.channels_max = 1; -]]> - - - - - - Typically, you'll have a hardware descriptor as below: - - - - - - - - - - - The info field contains the type and - capabilities of this pcm. The bit flags are defined in - <sound/asound.h> as - SNDRV_PCM_INFO_XXX. Here, at least, you - have to specify whether the mmap is supported and which - interleaved format is supported. - When the is supported, add the - SNDRV_PCM_INFO_MMAP flag here. When the - hardware supports the interleaved or the non-interleaved - formats, SNDRV_PCM_INFO_INTERLEAVED or - SNDRV_PCM_INFO_NONINTERLEAVED flag must - be set, respectively. If both are supported, you can set both, - too. - - - - In the above example, MMAP_VALID and - BLOCK_TRANSFER are specified for the OSS mmap - mode. Usually both are set. Of course, - MMAP_VALID is set only if the mmap is - really supported. - - - - The other possible flags are - SNDRV_PCM_INFO_PAUSE and - SNDRV_PCM_INFO_RESUME. The - PAUSE bit means that the pcm supports the - pause operation, while the - RESUME bit means that the pcm supports - the full suspend/resume operation. - If the PAUSE flag is set, - the trigger callback below - must handle the corresponding (pause push/release) commands. - The suspend/resume trigger commands can be defined even without - the RESUME flag. See - Power Management section for details. - - - - When the PCM substreams can be synchronized (typically, - synchronized start/stop of a playback and a capture streams), - you can give SNDRV_PCM_INFO_SYNC_START, - too. In this case, you'll need to check the linked-list of - PCM substreams in the trigger callback. This will be - described in the later section. - - - - - - formats field contains the bit-flags - of supported formats (SNDRV_PCM_FMTBIT_XXX). - If the hardware supports more than one format, give all or'ed - bits. In the example above, the signed 16bit little-endian - format is specified. - - - - - - rates field contains the bit-flags of - supported rates (SNDRV_PCM_RATE_XXX). - When the chip supports continuous rates, pass - CONTINUOUS bit additionally. - The pre-defined rate bits are provided only for typical - rates. If your chip supports unconventional rates, you need to add - the KNOT bit and set up the hardware - constraint manually (explained later). - - - - - - rate_min and - rate_max define the minimum and - maximum sample rate. This should correspond somehow to - rates bits. - - - - - - channel_min and - channel_max - define, as you might already expected, the minimum and maximum - number of channels. - - - - - - buffer_bytes_max defines the - maximum buffer size in bytes. There is no - buffer_bytes_min field, since - it can be calculated from the minimum period size and the - minimum number of periods. - Meanwhile, period_bytes_min and - define the minimum and maximum size of the period in bytes. - periods_max and - periods_min define the maximum and - minimum number of periods in the buffer. - - - - The period is a term that corresponds to - a fragment in the OSS world. The period defines the size at - which a PCM interrupt is generated. This size strongly - depends on the hardware. - Generally, the smaller period size will give you more - interrupts, that is, more controls. - In the case of capture, this size defines the input latency. - On the other hand, the whole buffer size defines the - output latency for the playback direction. - - - - - - There is also a field fifo_size. - This specifies the size of the hardware FIFO, but currently it - is neither used in the driver nor in the alsa-lib. So, you - can ignore this field. - - - - -
- -
- PCM Configurations - - Ok, let's go back again to the PCM runtime records. - The most frequently referred records in the runtime instance are - the PCM configurations. - The PCM configurations are stored in the runtime instance - after the application sends hw_params data via - alsa-lib. There are many fields copied from hw_params and - sw_params structs. For example, - format holds the format type - chosen by the application. This field contains the enum value - SNDRV_PCM_FORMAT_XXX. - - - - One thing to be noted is that the configured buffer and period - sizes are stored in frames in the runtime. - In the ALSA world, 1 frame = channels * samples-size. - For conversion between frames and bytes, you can use the - frames_to_bytes() and - bytes_to_frames() helper functions. - - -period_size); -]]> - - - - - - Also, many software parameters (sw_params) are - stored in frames, too. Please check the type of the field. - snd_pcm_uframes_t is for the frames as unsigned - integer while snd_pcm_sframes_t is for the frames - as signed integer. - -
- -
- DMA Buffer Information - - The DMA buffer is defined by the following four fields, - dma_area, - dma_addr, - dma_bytes and - dma_private. - The dma_area holds the buffer - pointer (the logical address). You can call - memcpy from/to - this pointer. Meanwhile, dma_addr - holds the physical address of the buffer. This field is - specified only when the buffer is a linear buffer. - dma_bytes holds the size of buffer - in bytes. dma_private is used for - the ALSA DMA allocator. - - - - If you use a standard ALSA function, - snd_pcm_lib_malloc_pages(), for - allocating the buffer, these fields are set by the ALSA middle - layer, and you should not change them by - yourself. You can read them but not write them. - On the other hand, if you want to allocate the buffer by - yourself, you'll need to manage it in hw_params callback. - At least, dma_bytes is mandatory. - dma_area is necessary when the - buffer is mmapped. If your driver doesn't support mmap, this - field is not necessary. dma_addr - is also optional. You can use - dma_private as you like, too. - -
- -
- Running Status - - The running status can be referred via runtime->status. - This is the pointer to the struct snd_pcm_mmap_status - record. For example, you can get the current DMA hardware - pointer via runtime->status->hw_ptr. - - - - The DMA application pointer can be referred via - runtime->control, which points to the - struct snd_pcm_mmap_control record. - However, accessing directly to this value is not recommended. - -
- -
- Private Data - - You can allocate a record for the substream and store it in - runtime->private_data. Usually, this - is done in - - the open callback. - Don't mix this with pcm->private_data. - The pcm->private_data usually points to the - chip instance assigned statically at the creation of PCM, while the - runtime->private_data points to a dynamic - data structure created at the PCM open callback. - - - -runtime->private_data = data; - .... - } -]]> - - - - - - The allocated object must be released in - - the close callback. - -
- -
- Interrupt Callbacks - - The field transfer_ack_begin and - transfer_ack_end are called at - the beginning and at the end of - snd_pcm_period_elapsed(), respectively. - -
- -
- -
- Operators - - OK, now let me give details about each pcm callback - (ops). In general, every callback must - return 0 if successful, or a negative error number - such as -EINVAL. To choose an appropriate - error number, it is advised to check what value other parts of - the kernel return when the same kind of request fails. - - - - The callback function takes at least the argument with - snd_pcm_substream pointer. To retrieve - the chip record from the given substream instance, you can use the - following macro. - - - - - - - - The macro reads substream->private_data, - which is a copy of pcm->private_data. - You can override the former if you need to assign different data - records per PCM substream. For example, the cmi8330 driver assigns - different private_data for playback and capture directions, - because it uses two different codecs (SB- and AD-compatible) for - different directions. - - -
- open callback - - - - - - - - This is called when a pcm substream is opened. - - - - At least, here you have to initialize the runtime->hw - record. Typically, this is done by like this: - - - -runtime; - - runtime->hw = snd_mychip_playback_hw; - return 0; - } -]]> - - - - where snd_mychip_playback_hw is the - pre-defined hardware description. - - - - You can allocate a private data in this callback, as described - in - Private Data section. - - - - If the hardware configuration needs more constraints, set the - hardware constraints here, too. - See - Constraints for more details. - -
- -
- close callback - - - - - - - - Obviously, this is called when a pcm substream is closed. - - - - Any private instance for a pcm substream allocated in the - open callback will be released here. - - - -runtime->private_data); - .... - } -]]> - - - -
- -
- ioctl callback - - This is used for any special call to pcm ioctls. But - usually you can pass a generic ioctl callback, - snd_pcm_lib_ioctl. - -
- -
- hw_params callback - - - - - - - - - - This is called when the hardware parameter - (hw_params) is set - up by the application, - that is, once when the buffer size, the period size, the - format, etc. are defined for the pcm substream. - - - - Many hardware setups should be done in this callback, - including the allocation of buffers. - - - - Parameters to be initialized are retrieved by - params_xxx() macros. To allocate - buffer, you can call a helper function, - - - - - - - - snd_pcm_lib_malloc_pages() is available - only when the DMA buffers have been pre-allocated. - See the section - Buffer Types for more details. - - - - Note that this and prepare callbacks - may be called multiple times per initialization. - For example, the OSS emulation may - call these callbacks at each change via its ioctl. - - - - Thus, you need to be careful not to allocate the same buffers - many times, which will lead to memory leaks! Calling the - helper function above many times is OK. It will release the - previous buffer automatically when it was already allocated. - - - - Another note is that this callback is non-atomic - (schedulable). This is important, because the - trigger callback - is atomic (non-schedulable). That is, mutexes or any - schedule-related functions are not available in - trigger callback. - Please see the subsection - - Atomicity for details. - -
- -
- hw_free callback - - - - - - - - - - This is called to release the resources allocated via - hw_params. For example, releasing the - buffer via - snd_pcm_lib_malloc_pages() is done by - calling the following: - - - - - - - - - - This function is always called before the close callback is called. - Also, the callback may be called multiple times, too. - Keep track whether the resource was already released. - -
- -
- prepare callback - - - - - - - - - - This callback is called when the pcm is - prepared. You can set the format type, sample - rate, etc. here. The difference from - hw_params is that the - prepare callback will be called each - time - snd_pcm_prepare() is called, i.e. when - recovering after underruns, etc. - - - - Note that this callback is now non-atomic. - You can use schedule-related functions safely in this callback. - - - - In this and the following callbacks, you can refer to the - values via the runtime record, - substream->runtime. - For example, to get the current - rate, format or channels, access to - runtime->rate, - runtime->format or - runtime->channels, respectively. - The physical address of the allocated buffer is set to - runtime->dma_area. The buffer and period sizes are - in runtime->buffer_size and runtime->period_size, - respectively. - - - - Be careful that this callback will be called many times at - each setup, too. - -
- -
- trigger callback - - - - - - - - This is called when the pcm is started, stopped or paused. - - - - Which action is specified in the second argument, - SNDRV_PCM_TRIGGER_XXX in - <sound/pcm.h>. At least, - the START and STOP - commands must be defined in this callback. - - - - - - - - - - When the pcm supports the pause operation (given in the info - field of the hardware table), the PAUSE_PUSE - and PAUSE_RELEASE commands must be - handled here, too. The former is the command to pause the pcm, - and the latter to restart the pcm again. - - - - When the pcm supports the suspend/resume operation, - regardless of full or partial suspend/resume support, - the SUSPEND and RESUME - commands must be handled, too. - These commands are issued when the power-management status is - changed. Obviously, the SUSPEND and - RESUME commands - suspend and resume the pcm substream, and usually, they - are identical to the STOP and - START commands, respectively. - See the - Power Management section for details. - - - - As mentioned, this callback is atomic. You cannot call - functions which may sleep. - The trigger callback should be as minimal as possible, - just really triggering the DMA. The other stuff should be - initialized hw_params and prepare callbacks properly - beforehand. - -
- -
- pointer callback - - - - - - - - This callback is called when the PCM middle layer inquires - the current hardware position on the buffer. The position must - be returned in frames, - ranging from 0 to buffer_size - 1. - - - - This is called usually from the buffer-update routine in the - pcm middle layer, which is invoked when - snd_pcm_period_elapsed() is called in the - interrupt routine. Then the pcm middle layer updates the - position and calculates the available space, and wakes up the - sleeping poll threads, etc. - - - - This callback is also atomic. - -
- -
- copy and silence callbacks - - These callbacks are not mandatory, and can be omitted in - most cases. These callbacks are used when the hardware buffer - cannot be in the normal memory space. Some chips have their - own buffer on the hardware which is not mappable. In such a - case, you have to transfer the data manually from the memory - buffer to the hardware buffer. Or, if the buffer is - non-contiguous on both physical and virtual memory spaces, - these callbacks must be defined, too. - - - - If these two callbacks are defined, copy and set-silence - operations are done by them. The detailed will be described in - the later section Buffer and Memory - Management. - -
- -
- ack callback - - This callback is also not mandatory. This callback is called - when the appl_ptr is updated in read or write operations. - Some drivers like emu10k1-fx and cs46xx need to track the - current appl_ptr for the internal buffer, and this callback - is useful only for such a purpose. - - - This callback is atomic. - -
- -
- page callback - - - This callback is optional too. This callback is used - mainly for non-contiguous buffers. The mmap calls this - callback to get the page address. Some examples will be - explained in the later section Buffer and Memory - Management, too. - -
-
- -
- Interrupt Handler - - The rest of pcm stuff is the PCM interrupt handler. The - role of PCM interrupt handler in the sound driver is to update - the buffer position and to tell the PCM middle layer when the - buffer position goes across the prescribed period size. To - inform this, call the snd_pcm_period_elapsed() - function. - - - - There are several types of sound chips to generate the interrupts. - - -
- Interrupts at the period (fragment) boundary - - This is the most frequently found type: the hardware - generates an interrupt at each period boundary. - In this case, you can call - snd_pcm_period_elapsed() at each - interrupt. - - - - snd_pcm_period_elapsed() takes the - substream pointer as its argument. Thus, you need to keep the - substream pointer accessible from the chip instance. For - example, define substream field in the chip record to hold the - current running substream pointer, and set the pointer value - at open callback (and reset at close callback). - - - - If you acquire a spinlock in the interrupt handler, and the - lock is used in other pcm callbacks, too, then you have to - release the lock before calling - snd_pcm_period_elapsed(), because - snd_pcm_period_elapsed() calls other pcm - callbacks inside. - - - - Typical code would be like: - - - Interrupt Handler Case #1 - -lock); - .... - if (pcm_irq_invoked(chip)) { - /* call updater, unlock before it */ - spin_unlock(&chip->lock); - snd_pcm_period_elapsed(chip->substream); - spin_lock(&chip->lock); - /* acknowledge the interrupt if necessary */ - } - .... - spin_unlock(&chip->lock); - return IRQ_HANDLED; - } -]]> - - - -
- -
- High frequency timer interrupts - - This happense when the hardware doesn't generate interrupts - at the period boundary but issues timer interrupts at a fixed - timer rate (e.g. es1968 or ymfpci drivers). - In this case, you need to check the current hardware - position and accumulate the processed sample length at each - interrupt. When the accumulated size exceeds the period - size, call - snd_pcm_period_elapsed() and reset the - accumulator. - - - - Typical code would be like the following. - - - Interrupt Handler Case #2 - -lock); - .... - if (pcm_irq_invoked(chip)) { - unsigned int last_ptr, size; - /* get the current hardware pointer (in frames) */ - last_ptr = get_hw_ptr(chip); - /* calculate the processed frames since the - * last update - */ - if (last_ptr < chip->last_ptr) - size = runtime->buffer_size + last_ptr - - chip->last_ptr; - else - size = last_ptr - chip->last_ptr; - /* remember the last updated point */ - chip->last_ptr = last_ptr; - /* accumulate the size */ - chip->size += size; - /* over the period boundary? */ - if (chip->size >= runtime->period_size) { - /* reset the accumulator */ - chip->size %= runtime->period_size; - /* call updater */ - spin_unlock(&chip->lock); - snd_pcm_period_elapsed(substream); - spin_lock(&chip->lock); - } - /* acknowledge the interrupt if necessary */ - } - .... - spin_unlock(&chip->lock); - return IRQ_HANDLED; - } -]]> - - - -
- -
- On calling <function>snd_pcm_period_elapsed()</function> - - In both cases, even if more than one period are elapsed, you - don't have to call - snd_pcm_period_elapsed() many times. Call - only once. And the pcm layer will check the current hardware - pointer and update to the latest status. - -
-
- -
- Atomicity - - One of the most important (and thus difficult to debug) problems - in kernel programming are race conditions. - In the Linux kernel, they are usually avoided via spin-locks, mutexes - or semaphores. In general, if a race condition can happen - in an interrupt handler, it has to be managed atomically, and you - have to use a spinlock to protect the critical session. If the - critical section is not in interrupt handler code and - if taking a relatively long time to execute is acceptable, you - should use mutexes or semaphores instead. - - - - As already seen, some pcm callbacks are atomic and some are - not. For example, the hw_params callback is - non-atomic, while trigger callback is - atomic. This means, the latter is called already in a spinlock - held by the PCM middle layer. Please take this atomicity into - account when you choose a locking scheme in the callbacks. - - - - In the atomic callbacks, you cannot use functions which may call - schedule or go to - sleep. Semaphores and mutexes can sleep, - and hence they cannot be used inside the atomic callbacks - (e.g. trigger callback). - To implement some delay in such a callback, please use - udelay() or mdelay(). - - - - All three atomic callbacks (trigger, pointer, and ack) are - called with local interrupts disabled. - - -
-
- Constraints - - If your chip supports unconventional sample rates, or only the - limited samples, you need to set a constraint for the - condition. - - - - For example, in order to restrict the sample rates in the some - supported values, use - snd_pcm_hw_constraint_list(). - You need to call this function in the open callback. - - - Example of Hardware Constraints - -runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_rates); - if (err < 0) - return err; - .... - } -]]> - - - - - - There are many different constraints. - Look at sound/pcm.h for a complete list. - You can even define your own constraint rules. - For example, let's suppose my_chip can manage a substream of 1 channel - if and only if the format is S16_LE, otherwise it supports any format - specified in the snd_pcm_hardware structure (or in any - other constraint_list). You can build a rule like this: - - - Example of Hardware Constraints for Channels - -min < 2) { - fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE; - return snd_mask_refine(f, &fmt); - } - return 0; - } -]]> - - - - - - Then you need to call this function to add your rule: - - - -runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - hw_rule_channels_by_format, 0, SNDRV_PCM_HW_PARAM_FORMAT, - -1); -]]> - - - - - - The rule function is called when an application sets the number of - channels. But an application can set the format before the number of - channels. Thus you also need to define the inverse rule: - - - Example of Hardware Constraints for Channels - -bits[0] == SNDRV_PCM_FMTBIT_S16_LE) { - ch.min = ch.max = 1; - ch.integer = 1; - return snd_interval_refine(c, &ch); - } - return 0; - } -]]> - - - - - - ...and in the open callback: - - -runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - hw_rule_format_by_channels, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - -1); -]]> - - - - - - I won't give more details here, rather I - would like to say, Luke, use the source. - -
- -
- - - - - - - Control Interface - -
- General - - The control interface is used widely for many switches, - sliders, etc. which are accessed from user-space. Its most - important use is the mixer interface. In other words, since ALSA - 0.9.x, all the mixer stuff is implemented on the control kernel API. - - - - ALSA has a well-defined AC97 control module. If your chip - supports only the AC97 and nothing else, you can skip this - section. - - - - The control API is defined in - <sound/control.h>. - Include this file if you want to add your own controls. - -
- -
- Definition of Controls - - To create a new control, you need to define the - following three - callbacks: info, - get and - put. Then, define a - struct snd_kcontrol_new record, such as: - - - Definition of a Control - - - - - - - - Most likely the control is created via - snd_ctl_new1(), and in such a case, you can - add the __devinitdata prefix to the - definition as above. - - - - The iface field specifies the control - type, SNDRV_CTL_ELEM_IFACE_XXX, which - is usually MIXER. - Use CARD for global controls that are not - logically part of the mixer. - If the control is closely associated with some specific device on - the sound card, use HWDEP, - PCM, RAWMIDI, - TIMER, or SEQUENCER, and - specify the device number with the - device and - subdevice fields. - - - - The name is the name identifier - string. Since ALSA 0.9.x, the control name is very important, - because its role is classified from its name. There are - pre-defined standard control names. The details are described in - the - Control Names subsection. - - - - The index field holds the index number - of this control. If there are several different controls with - the same name, they can be distinguished by the index - number. This is the case when - several codecs exist on the card. If the index is zero, you can - omit the definition above. - - - - The access field contains the access - type of this control. Give the combination of bit masks, - SNDRV_CTL_ELEM_ACCESS_XXX, there. - The details will be explained in - the - Access Flags subsection. - - - - The private_value field contains - an arbitrary long integer value for this record. When using - the generic info, - get and - put callbacks, you can pass a value - through this field. If several small numbers are necessary, you can - combine them in bitwise. Or, it's possible to give a pointer - (casted to unsigned long) of some record to this field, too. - - - - The tlv field can be used to provide - metadata about the control; see the - - Metadata subsection. - - - - The other three are - - callback functions. - -
- -
- Control Names - - There are some standards to define the control names. A - control is usually defined from the three parts as - SOURCE DIRECTION FUNCTION. - - - - The first, SOURCE, specifies the source - of the control, and is a string such as Master, - PCM, CD and - Line. There are many pre-defined sources. - - - - The second, DIRECTION, is one of the - following strings according to the direction of the control: - Playback, Capture, Bypass - Playback and Bypass Capture. Or, it can - be omitted, meaning both playback and capture directions. - - - - The third, FUNCTION, is one of the - following strings according to the function of the control: - Switch, Volume and - Route. - - - - The example of control names are, thus, Master Capture - Switch or PCM Playback Volume. - - - - There are some exceptions: - - -
- Global capture and playback - - Capture Source, Capture Switch - and Capture Volume are used for the global - capture (input) source, switch and volume. Similarly, - Playback Switch and Playback - Volume are used for the global output gain switch and - volume. - -
- -
- Tone-controls - - tone-control switch and volumes are specified like - Tone Control - XXX, e.g. Tone Control - - Switch, Tone Control - Bass, - Tone Control - Center. - -
- -
- 3D controls - - 3D-control switches and volumes are specified like 3D - Control - XXX, e.g. 3D Control - - Switch, 3D Control - Center, 3D - Control - Space. - -
- -
- Mic boost - - Mic-boost switch is set as Mic Boost or - Mic Boost (6dB). - - - - More precise information can be found in - Documentation/sound/alsa/ControlNames.txt. - -
-
- -
- Access Flags - - - The access flag is the bitmask which specifies the access type - of the given control. The default access type is - SNDRV_CTL_ELEM_ACCESS_READWRITE, - which means both read and write are allowed to this control. - When the access flag is omitted (i.e. = 0), it is - considered as READWRITE access as default. - - - - When the control is read-only, pass - SNDRV_CTL_ELEM_ACCESS_READ instead. - In this case, you don't have to define - the put callback. - Similarly, when the control is write-only (although it's a rare - case), you can use the WRITE flag instead, and - you don't need the get callback. - - - - If the control value changes frequently (e.g. the VU meter), - VOLATILE flag should be given. This means - that the control may be changed without - - notification. Applications should poll such - a control constantly. - - - - When the control is inactive, set - the INACTIVE flag, too. - There are LOCK and - OWNER flags to change the write - permissions. - - -
- -
- Callbacks - -
- info callback - - The info callback is used to get - detailed information on this control. This must store the - values of the given struct snd_ctl_elem_info - object. For example, for a boolean control with a single - element: - - - Example of info callback - -type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; - } -]]> - - - - - - The type field specifies the type - of the control. There are BOOLEAN, - INTEGER, ENUMERATED, - BYTES, IEC958 and - INTEGER64. The - count field specifies the - number of elements in this control. For example, a stereo - volume would have count = 2. The - value field is a union, and - the values stored are depending on the type. The boolean and - integer types are identical. - - - - The enumerated type is a bit different from others. You'll - need to set the string for the currently given item index. - - - -type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; - } -]]> - - - - - - Some common info callbacks are available for your convenience: - snd_ctl_boolean_mono_info() and - snd_ctl_boolean_stereo_info(). - Obviously, the former is an info callback for a mono channel - boolean item, just like snd_myctl_mono_info - above, and the latter is for a stereo channel boolean item. - - -
- -
- get callback - - - This callback is used to read the current value of the - control and to return to user-space. - - - - For example, - - - Example of get callback - -value.integer.value[0] = get_some_value(chip); - return 0; - } -]]> - - - - - - The value field depends on - the type of control as well as on the info callback. For example, - the sb driver uses this field to store the register offset, - the bit-shift and the bit-mask. The - private_value field is set as follows: - - - - - - and is retrieved in callbacks like - - -private_value & 0xff; - int shift = (kcontrol->private_value >> 16) & 0xff; - int mask = (kcontrol->private_value >> 24) & 0xff; - .... - } -]]> - - - - - - In the get callback, - you have to fill all the elements if the - control has more than one elements, - i.e. count > 1. - In the example above, we filled only one element - (value.integer.value[0]) since it's - assumed as count = 1. - -
- -
- put callback - - - This callback is used to write a value from user-space. - - - - For example, - - - Example of put callback - -current_value != - ucontrol->value.integer.value[0]) { - change_current_value(chip, - ucontrol->value.integer.value[0]); - changed = 1; - } - return changed; - } -]]> - - - - As seen above, you have to return 1 if the value is - changed. If the value is not changed, return 0 instead. - If any fatal error happens, return a negative error code as - usual. - - - - As in the get callback, - when the control has more than one elements, - all elements must be evaluated in this callback, too. - -
- -
- Callbacks are not atomic - - All these three callbacks are basically not atomic. - -
-
- -
- Constructor - - When everything is ready, finally we can create a new - control. To create a control, there are two functions to be - called, snd_ctl_new1() and - snd_ctl_add(). - - - - In the simplest way, you can do like this: - - - - - - - - where my_control is the - struct snd_kcontrol_new object defined above, and chip - is the object pointer to be passed to - kcontrol->private_data - which can be referred to in callbacks. - - - - snd_ctl_new1() allocates a new - snd_kcontrol instance (that's why the definition - of my_control can be with - the __devinitdata - prefix), and snd_ctl_add assigns the given - control component to the card. - -
- -
- Change Notification - - If you need to change and update a control in the interrupt - routine, you can call snd_ctl_notify(). For - example, - - - - - - - - This function takes the card pointer, the event-mask, and the - control id pointer for the notification. The event-mask - specifies the types of notification, for example, in the above - example, the change of control values is notified. - The id pointer is the pointer of struct snd_ctl_elem_id - to be notified. - You can find some examples in es1938.c or - es1968.c for hardware volume interrupts. - -
- -
- Metadata - - To provide information about the dB values of a mixer control, use - on of the DECLARE_TLV_xxx macros from - <sound/tlv.h> to define a variable - containing this information, set thetlv.p - field to point to this variable, and include the - SNDRV_CTL_ELEM_ACCESS_TLV_READ flag in the - access field; like this: - - - - - - - - - The DECLARE_TLV_DB_SCALE macro defines - information about a mixer control where each step in the control's - value changes the dB value by a constant dB amount. - The first parameter is the name of the variable to be defined. - The second parameter is the minimum value, in units of 0.01 dB. - The third parameter is the step size, in units of 0.01 dB. - Set the fourth parameter to 1 if the minimum value actually mutes - the control. - - - - The DECLARE_TLV_DB_LINEAR macro defines - information about a mixer control where the control's value affects - the output linearly. - The first parameter is the name of the variable to be defined. - The second parameter is the minimum value, in units of 0.01 dB. - The third parameter is the maximum value, in units of 0.01 dB. - If the minimum value mutes the control, set the second parameter to - TLV_DB_GAIN_MUTE. - -
- -
- - - - - - - API for AC97 Codec - -
- General - - The ALSA AC97 codec layer is a well-defined one, and you don't - have to write much code to control it. Only low-level control - routines are necessary. The AC97 codec API is defined in - <sound/ac97_codec.h>. - -
- -
- Full Code Example - - - Example of AC97 Interface - -private_data; - .... - /* read a register value here from the codec */ - return the_register_value; - } - - static void snd_mychip_ac97_write(struct snd_ac97 *ac97, - unsigned short reg, unsigned short val) - { - struct mychip *chip = ac97->private_data; - .... - /* write the given register value to the codec */ - } - - static int snd_mychip_ac97(struct mychip *chip) - { - struct snd_ac97_bus *bus; - struct snd_ac97_template ac97; - int err; - static struct snd_ac97_bus_ops ops = { - .write = snd_mychip_ac97_write, - .read = snd_mychip_ac97_read, - }; - - err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus); - if (err < 0) - return err; - memset(&ac97, 0, sizeof(ac97)); - ac97.private_data = chip; - return snd_ac97_mixer(bus, &ac97, &chip->ac97); - } - -]]> - - - -
- -
- Constructor - - To create an ac97 instance, first call snd_ac97_bus - with an ac97_bus_ops_t record with callback functions. - - - - - - - - The bus record is shared among all belonging ac97 instances. - - - - And then call snd_ac97_mixer() with an - struct snd_ac97_template - record together with the bus pointer created above. - - - -ac97); -]]> - - - - where chip->ac97 is a pointer to a newly created - ac97_t instance. - In this case, the chip pointer is set as the private data, so that - the read/write callback functions can refer to this chip instance. - This instance is not necessarily stored in the chip - record. If you need to change the register values from the - driver, or need the suspend/resume of ac97 codecs, keep this - pointer to pass to the corresponding functions. - -
- -
- Callbacks - - The standard callbacks are read and - write. Obviously they - correspond to the functions for read and write accesses to the - hardware low-level codes. - - - - The read callback returns the - register value specified in the argument. - - - -private_data; - .... - return the_register_value; - } -]]> - - - - Here, the chip can be cast from ac97->private_data. - - - - Meanwhile, the write callback is - used to set the register value. - - - - - - - - - - These callbacks are non-atomic like the control API callbacks. - - - - There are also other callbacks: - reset, - wait and - init. - - - - The reset callback is used to reset - the codec. If the chip requires a special kind of reset, you can - define this callback. - - - - The wait callback is used to - add some waiting time in the standard initialization of the codec. If the - chip requires the extra waiting time, define this callback. - - - - The init callback is used for - additional initialization of the codec. - -
- -
- Updating Registers in The Driver - - If you need to access to the codec from the driver, you can - call the following functions: - snd_ac97_write(), - snd_ac97_read(), - snd_ac97_update() and - snd_ac97_update_bits(). - - - - Both snd_ac97_write() and - snd_ac97_update() functions are used to - set a value to the given register - (AC97_XXX). The difference between them is - that snd_ac97_update() doesn't write a - value if the given value has been already set, while - snd_ac97_write() always rewrites the - value. - - - - - - - - - - snd_ac97_read() is used to read the value - of the given register. For example, - - - - - - - - - - snd_ac97_update_bits() is used to update - some bits in the given register. - - - - - - - - - - Also, there is a function to change the sample rate (of a - given register such as - AC97_PCM_FRONT_DAC_RATE) when VRA or - DRA is supported by the codec: - snd_ac97_set_rate(). - - - - - - - - - - The following registers are available to set the rate: - AC97_PCM_MIC_ADC_RATE, - AC97_PCM_FRONT_DAC_RATE, - AC97_PCM_LR_ADC_RATE, - AC97_SPDIF. When - AC97_SPDIF is specified, the register is - not really changed but the corresponding IEC958 status bits will - be updated. - -
- -
- Clock Adjustment - - In some chips, the clock of the codec isn't 48000 but using a - PCI clock (to save a quartz!). In this case, change the field - bus->clock to the corresponding - value. For example, intel8x0 - and es1968 drivers have their own function to read from the clock. - -
- -
- Proc Files - - The ALSA AC97 interface will create a proc file such as - /proc/asound/card0/codec97#0/ac97#0-0 and - ac97#0-0+regs. You can refer to these files to - see the current status and registers of the codec. - -
- -
- Multiple Codecs - - When there are several codecs on the same card, you need to - call snd_ac97_mixer() multiple times with - ac97.num=1 or greater. The num field - specifies the codec number. - - - - If you set up multiple codecs, you either need to write - different callbacks for each codec or check - ac97->num in the callback routines. - -
- -
- - - - - - - MIDI (MPU401-UART) Interface - -
- General - - Many soundcards have built-in MIDI (MPU401-UART) - interfaces. When the soundcard supports the standard MPU401-UART - interface, most likely you can use the ALSA MPU401-UART API. The - MPU401-UART API is defined in - <sound/mpu401.h>. - - - - Some soundchips have a similar but slightly different - implementation of mpu401 stuff. For example, emu10k1 has its own - mpu401 routines. - -
- -
- Constructor - - To create a rawmidi object, call - snd_mpu401_uart_new(). - - - - - - - - - - The first argument is the card pointer, and the second is the - index of this component. You can create up to 8 rawmidi - devices. - - - - The third argument is the type of the hardware, - MPU401_HW_XXX. If it's not a special one, - you can use MPU401_HW_MPU401. - - - - The 4th argument is the I/O port address. Many - backward-compatible MPU401 have an I/O port such as 0x330. Or, it - might be a part of its own PCI I/O region. It depends on the - chip design. - - - - The 5th argument is a bitflag for additional information. - When the I/O port address above is part of the PCI I/O - region, the MPU401 I/O port might have been already allocated - (reserved) by the driver itself. In such a case, pass a bit flag - MPU401_INFO_INTEGRATED, - and the mpu401-uart layer will allocate the I/O ports by itself. - - - - When the controller supports only the input or output MIDI stream, - pass the MPU401_INFO_INPUT or - MPU401_INFO_OUTPUT bitflag, respectively. - Then the rawmidi instance is created as a single stream. - - - - MPU401_INFO_MMIO bitflag is used to change - the access method to MMIO (via readb and writeb) instead of - iob and outb. In this case, you have to pass the iomapped address - to snd_mpu401_uart_new(). - - - - When MPU401_INFO_TX_IRQ is set, the output - stream isn't checked in the default interrupt handler. The driver - needs to call snd_mpu401_uart_interrupt_tx() - by itself to start processing the output stream in the irq handler. - - - - Usually, the port address corresponds to the command port and - port + 1 corresponds to the data port. If not, you may change - the cport field of - struct snd_mpu401 manually - afterward. However, snd_mpu401 pointer is not - returned explicitly by - snd_mpu401_uart_new(). You need to cast - rmidi->private_data to - snd_mpu401 explicitly, - - - -private_data; -]]> - - - - and reset the cport as you like: - - - -cport = my_own_control_port; -]]> - - - - - - The 6th argument specifies the irq number for UART. If the irq - is already allocated, pass 0 to the 7th argument - (irq_flags). Otherwise, pass the flags - for irq allocation - (SA_XXX bits) to it, and the irq will be - reserved by the mpu401-uart layer. If the card doesn't generate - UART interrupts, pass -1 as the irq number. Then a timer - interrupt will be invoked for polling. - -
- -
- Interrupt Handler - - When the interrupt is allocated in - snd_mpu401_uart_new(), the private - interrupt handler is used, hence you don't have anything else to do - than creating the mpu401 stuff. Otherwise, you have to call - snd_mpu401_uart_interrupt() explicitly when - a UART interrupt is invoked and checked in your own interrupt - handler. - - - - In this case, you need to pass the private_data of the - returned rawmidi object from - snd_mpu401_uart_new() as the second - argument of snd_mpu401_uart_interrupt(). - - - -private_data, regs); -]]> - - - -
- -
- - - - - - - RawMIDI Interface - -
- Overview - - - The raw MIDI interface is used for hardware MIDI ports that can - be accessed as a byte stream. It is not used for synthesizer - chips that do not directly understand MIDI. - - - - ALSA handles file and buffer management. All you have to do is - to write some code to move data between the buffer and the - hardware. - - - - The rawmidi API is defined in - <sound/rawmidi.h>. - -
- -
- Constructor - - - To create a rawmidi device, call the - snd_rawmidi_new function: - - -card, "MyMIDI", 0, outs, ins, &rmidi); - if (err < 0) - return err; - rmidi->private_data = chip; - strcpy(rmidi->name, "My MIDI"); - rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; -]]> - - - - - - The first argument is the card pointer, the second argument is - the ID string. - - - - The third argument is the index of this component. You can - create up to 8 rawmidi devices. - - - - The fourth and fifth arguments are the number of output and - input substreams, respectively, of this device (a substream is - the equivalent of a MIDI port). - - - - Set the info_flags field to specify - the capabilities of the device. - Set SNDRV_RAWMIDI_INFO_OUTPUT if there is - at least one output port, - SNDRV_RAWMIDI_INFO_INPUT if there is at - least one input port, - and SNDRV_RAWMIDI_INFO_DUPLEX if the device - can handle output and input at the same time. - - - - After the rawmidi device is created, you need to set the - operators (callbacks) for each substream. There are helper - functions to set the operators for all the substreams of a device: - - - - - - - - - The operators are usually defined like this: - - - - - - These callbacks are explained in the Callbacks - section. - - - - If there are more than one substream, you should give a - unique name to each of them: - - -streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams, - list { - sprintf(substream->name, "My MIDI Port %d", substream->number + 1); - } - /* same for SNDRV_RAWMIDI_STREAM_INPUT */ -]]> - - - -
- -
- Callbacks - - - In all the callbacks, the private data that you've set for the - rawmidi device can be accessed as - substream->rmidi->private_data. - - - - - If there is more than one port, your callbacks can determine the - port index from the struct snd_rawmidi_substream data passed to each - callback: - - -number; -]]> - - - - -
- <function>open</function> callback - - - - - - - - - This is called when a substream is opened. - You can initialize the hardware here, but you shouldn't - start transmitting/receiving data yet. - -
- -
- <function>close</function> callback - - - - - - - - - Guess what. - - - - The open and close - callbacks of a rawmidi device are serialized with a mutex, - and can sleep. - -
- -
- <function>trigger</function> callback for output - substreams - - - - - - - - - This is called with a nonzero up - parameter when there is some data in the substream buffer that - must be transmitted. - - - - To read data from the buffer, call - snd_rawmidi_transmit_peek. It will - return the number of bytes that have been read; this will be - less than the number of bytes requested when there are no more - data in the buffer. - After the data have been transmitted successfully, call - snd_rawmidi_transmit_ack to remove the - data from the substream buffer: - - - - - - - - - If you know beforehand that the hardware will accept data, you - can use the snd_rawmidi_transmit function - which reads some data and removes them from the buffer at once: - - - - - - - - - If you know beforehand how many bytes you can accept, you can - use a buffer size greater than one with the - snd_rawmidi_transmit* functions. - - - - The trigger callback must not sleep. If - the hardware FIFO is full before the substream buffer has been - emptied, you have to continue transmitting data later, either - in an interrupt handler, or with a timer if the hardware - doesn't have a MIDI transmit interrupt. - - - - The trigger callback is called with a - zero up parameter when the transmission - of data should be aborted. - -
- -
- <function>trigger</function> callback for input - substreams - - - - - - - - - This is called with a nonzero up - parameter to enable receiving data, or with a zero - up parameter do disable receiving data. - - - - The trigger callback must not sleep; the - actual reading of data from the device is usually done in an - interrupt handler. - - - - When data reception is enabled, your interrupt handler should - call snd_rawmidi_receive for all received - data: - - - - - - -
- -
- <function>drain</function> callback - - - - - - - - - This is only used with output substreams. This function should wait - until all data read from the substream buffer have been transmitted. - This ensures that the device can be closed and the driver unloaded - without losing data. - - - - This callback is optional. If you do not set - drain in the struct snd_rawmidi_ops - structure, ALSA will simply wait for 50 milliseconds - instead. - -
-
- -
- - - - - - - Miscellaneous Devices - -
- FM OPL3 - - The FM OPL3 is still used in many chips (mainly for backward - compatibility). ALSA has a nice OPL3 FM control layer, too. The - OPL3 API is defined in - <sound/opl3.h>. - - - - FM registers can be directly accessed through the direct-FM API, - defined in <sound/asound_fm.h>. In - ALSA native mode, FM registers are accessed through - the Hardware-Dependant Device direct-FM extension API, whereas in - OSS compatible mode, FM registers can be accessed with the OSS - direct-FM compatible API in /dev/dmfmX device. - - - - To create the OPL3 component, you have two functions to - call. The first one is a constructor for the opl3_t - instance. - - - - - - - - - - The first argument is the card pointer, the second one is the - left port address, and the third is the right port address. In - most cases, the right port is placed at the left port + 2. - - - - The fourth argument is the hardware type. - - - - When the left and right ports have been already allocated by - the card driver, pass non-zero to the fifth argument - (integrated). Otherwise, the opl3 module will - allocate the specified ports by itself. - - - - When the accessing the hardware requires special method - instead of the standard I/O access, you can create opl3 instance - separately with snd_opl3_new(). - - - - - - - - - - Then set command, - private_data and - private_free for the private - access function, the private data and the destructor. - The l_port and r_port are not necessarily set. Only the - command must be set properly. You can retrieve the data - from the opl3->private_data field. - - - - After creating the opl3 instance via snd_opl3_new(), - call snd_opl3_init() to initialize the chip to the - proper state. Note that snd_opl3_create() always - calls it internally. - - - - If the opl3 instance is created successfully, then create a - hwdep device for this opl3. - - - - - - - - - - The first argument is the opl3_t instance you - created, and the second is the index number, usually 0. - - - - The third argument is the index-offset for the sequencer - client assigned to the OPL3 port. When there is an MPU401-UART, - give 1 for here (UART always takes 0). - -
- -
- Hardware-Dependent Devices - - Some chips need user-space access for special - controls or for loading the micro code. In such a case, you can - create a hwdep (hardware-dependent) device. The hwdep API is - defined in <sound/hwdep.h>. You can - find examples in opl3 driver or - isa/sb/sb16_csp.c. - - - - The creation of the hwdep instance is done via - snd_hwdep_new(). - - - - - - - - where the third argument is the index number. - - - - You can then pass any pointer value to the - private_data. - If you assign a private data, you should define the - destructor, too. The destructor function is set in - the private_free field. - - - -private_data = p; - hw->private_free = mydata_free; -]]> - - - - and the implementation of the destructor would be: - - - -private_data; - kfree(p); - } -]]> - - - - - - The arbitrary file operations can be defined for this - instance. The file operators are defined in - the ops table. For example, assume that - this chip needs an ioctl. - - - -ops.open = mydata_open; - hw->ops.ioctl = mydata_ioctl; - hw->ops.release = mydata_release; -]]> - - - - And implement the callback functions as you like. - -
- -
- IEC958 (S/PDIF) - - Usually the controls for IEC958 devices are implemented via - the control interface. There is a macro to compose a name string for - IEC958 controls, SNDRV_CTL_NAME_IEC958() - defined in <include/asound.h>. - - - - There are some standard controls for IEC958 status bits. These - controls use the type SNDRV_CTL_ELEM_TYPE_IEC958, - and the size of element is fixed as 4 bytes array - (value.iec958.status[x]). For the info - callback, you don't specify - the value field for this type (the count field must be set, - though). - - - - IEC958 Playback Con Mask is used to return the - bit-mask for the IEC958 status bits of consumer mode. Similarly, - IEC958 Playback Pro Mask returns the bitmask for - professional mode. They are read-only controls, and are defined - as MIXER controls (iface = - SNDRV_CTL_ELEM_IFACE_MIXER). - - - - Meanwhile, IEC958 Playback Default control is - defined for getting and setting the current default IEC958 - bits. Note that this one is usually defined as a PCM control - (iface = SNDRV_CTL_ELEM_IFACE_PCM), - although in some places it's defined as a MIXER control. - - - - In addition, you can define the control switches to - enable/disable or to set the raw bit mode. The implementation - will depend on the chip, but the control should be named as - IEC958 xxx, preferably using - the SNDRV_CTL_NAME_IEC958() macro. - - - - You can find several cases, for example, - pci/emu10k1, - pci/ice1712, or - pci/cmipci.c. - -
- -
- - - - - - - Buffer and Memory Management - -
- Buffer Types - - ALSA provides several different buffer allocation functions - depending on the bus and the architecture. All these have a - consistent API. The allocation of physically-contiguous pages is - done via - snd_malloc_xxx_pages() function, where xxx - is the bus type. - - - - The allocation of pages with fallback is - snd_malloc_xxx_pages_fallback(). This - function tries to allocate the specified pages but if the pages - are not available, it tries to reduce the page sizes until - enough space is found. - - - - The release the pages, call - snd_free_xxx_pages() function. - - - - Usually, ALSA drivers try to allocate and reserve - a large contiguous physical space - at the time the module is loaded for the later use. - This is called pre-allocation. - As already written, you can call the following function at - pcm instance construction time (in the case of PCI bus). - - - - - - - - where size is the byte size to be - pre-allocated and the max is the maximum - size to be changed via the prealloc proc file. - The allocator will try to get an area as large as possible - within the given size. - - - - The second argument (type) and the third argument (device pointer) - are dependent on the bus. - In the case of the ISA bus, pass snd_dma_isa_data() - as the third argument with SNDRV_DMA_TYPE_DEV type. - For the continuous buffer unrelated to the bus can be pre-allocated - with SNDRV_DMA_TYPE_CONTINUOUS type and the - snd_dma_continuous_data(GFP_KERNEL) device pointer, - where GFP_KERNEL is the kernel allocation flag to - use. - For the PCI scatter-gather buffers, use - SNDRV_DMA_TYPE_DEV_SG with - snd_dma_pci_data(pci) - (see the - Non-Contiguous Buffers - section). - - - - Once the buffer is pre-allocated, you can use the - allocator in the hw_params callback: - - - - - - - - Note that you have to pre-allocate to use this function. - -
- -
- External Hardware Buffers - - Some chips have their own hardware buffers and the DMA - transfer from the host memory is not available. In such a case, - you need to either 1) copy/set the audio data directly to the - external hardware buffer, or 2) make an intermediate buffer and - copy/set the data from it to the external hardware buffer in - interrupts (or in tasklets, preferably). - - - - The first case works fine if the external hardware buffer is large - enough. This method doesn't need any extra buffers and thus is - more effective. You need to define the - copy and - silence callbacks for - the data transfer. However, there is a drawback: it cannot - be mmapped. The examples are GUS's GF1 PCM or emu8000's - wavetable PCM. - - - - The second case allows for mmap on the buffer, although you have - to handle an interrupt or a tasklet to transfer the data - from the intermediate buffer to the hardware buffer. You can find an - example in the vxpocket driver. - - - - Another case is when the chip uses a PCI memory-map - region for the buffer instead of the host memory. In this case, - mmap is available only on certain architectures like the Intel one. - In non-mmap mode, the data cannot be transferred as in the normal - way. Thus you need to define the copy and - silence callbacks as well, - as in the cases above. The examples are found in - rme32.c and rme96.c. - - - - The implementation of the copy and - silence callbacks depends upon - whether the hardware supports interleaved or non-interleaved - samples. The copy callback is - defined like below, a bit - differently depending whether the direction is playback or - capture: - - - - - - - - - - In the case of interleaved samples, the second argument - (channel) is not used. The third argument - (pos) points the - current position offset in frames. - - - - The meaning of the fourth argument is different between - playback and capture. For playback, it holds the source data - pointer, and for capture, it's the destination data pointer. - - - - The last argument is the number of frames to be copied. - - - - What you have to do in this callback is again different - between playback and capture directions. In the - playback case, you copy the given amount of data - (count) at the specified pointer - (src) to the specified offset - (pos) on the hardware buffer. When - coded like memcpy-like way, the copy would be like: - - - - - - - - - - For the capture direction, you copy the given amount of - data (count) at the specified offset - (pos) on the hardware buffer to the - specified pointer (dst). - - - - - - - - Note that both the position and the amount of data are given - in frames. - - - - In the case of non-interleaved samples, the implementation - will be a bit more complicated. - - - - You need to check the channel argument, and if it's -1, copy - the whole channels. Otherwise, you have to copy only the - specified channel. Please check - isa/gus/gus_pcm.c as an example. - - - - The silence callback is also - implemented in a similar way. - - - - - - - - - - The meanings of arguments are the same as in the - copy - callback, although there is no src/dst - argument. In the case of interleaved samples, the channel - argument has no meaning, as well as on - copy callback. - - - - The role of silence callback is to - set the given amount - (count) of silence data at the - specified offset (pos) on the hardware - buffer. Suppose that the data format is signed (that is, the - silent-data is 0), and the implementation using a memset-like - function would be like: - - - - - - - - - - In the case of non-interleaved samples, again, the - implementation becomes a bit more complicated. See, for example, - isa/gus/gus_pcm.c. - -
- -
- Non-Contiguous Buffers - - If your hardware supports the page table as in emu10k1 or the - buffer descriptors as in via82xx, you can use the scatter-gather - (SG) DMA. ALSA provides an interface for handling SG-buffers. - The API is provided in <sound/pcm.h>. - - - - For creating the SG-buffer handler, call - snd_pcm_lib_preallocate_pages() or - snd_pcm_lib_preallocate_pages_for_all() - with SNDRV_DMA_TYPE_DEV_SG - in the PCM constructor like other PCI pre-allocator. - You need to pass snd_dma_pci_data(pci), - where pci is the struct pci_dev pointer - of the chip as well. - The struct snd_sg_buf instance is created as - substream->dma_private. You can cast - the pointer like: - - - -dma_private; -]]> - - - - - - Then call snd_pcm_lib_malloc_pages() - in the hw_params callback - as well as in the case of normal PCI buffer. - The SG-buffer handler will allocate the non-contiguous kernel - pages of the given size and map them onto the virtually contiguous - memory. The virtual pointer is addressed in runtime->dma_area. - The physical address (runtime->dma_addr) is set to zero, - because the buffer is physically non-contigous. - The physical address table is set up in sgbuf->table. - You can get the physical address at a certain offset via - snd_pcm_sgbuf_get_addr(). - - - - When a SG-handler is used, you need to set - snd_pcm_sgbuf_ops_page as - the page callback. - (See - page callback section.) - - - - To release the data, call - snd_pcm_lib_free_pages() in the - hw_free callback as usual. - -
- -
- Vmalloc'ed Buffers - - It's possible to use a buffer allocated via - vmalloc, for example, for an intermediate - buffer. Since the allocated pages are not contiguous, you need - to set the page callback to obtain - the physical address at every offset. - - - - The implementation of page callback - would be like this: - - - - - - /* get the physical page pointer on the given offset */ - static struct page *mychip_page(struct snd_pcm_substream *substream, - unsigned long offset) - { - void *pageptr = substream->runtime->dma_area + offset; - return vmalloc_to_page(pageptr); - } -]]> - - - -
- -
- - - - - - - Proc Interface - - ALSA provides an easy interface for procfs. The proc files are - very useful for debugging. I recommend you set up proc files if - you write a driver and want to get a running status or register - dumps. The API is found in - <sound/info.h>. - - - - To create a proc file, call - snd_card_proc_new(). - - - - - - - - where the second argument specifies the name of the proc file to be - created. The above example will create a file - my-file under the card directory, - e.g. /proc/asound/card0/my-file. - - - - Like other components, the proc entry created via - snd_card_proc_new() will be registered and - released automatically in the card registration and release - functions. - - - - When the creation is successful, the function stores a new - instance in the pointer given in the third argument. - It is initialized as a text proc file for read only. To use - this proc file as a read-only text file as it is, set the read - callback with a private data via - snd_info_set_text_ops(). - - - - - - - - where the second argument (chip) is the - private data to be used in the callbacks. The third parameter - specifies the read buffer size and the fourth - (my_proc_read) is the callback function, which - is defined like - - - - - - - - - - - In the read callback, use snd_iprintf() for - output strings, which works just like normal - printf(). For example, - - - -private_data; - - snd_iprintf(buffer, "This is my chip!\n"); - snd_iprintf(buffer, "Port = %ld\n", chip->port); - } -]]> - - - - - - The file permissions can be changed afterwards. As default, it's - set as read only for all users. If you want to add write - permission for the user (root as default), do as follows: - - - -mode = S_IFREG | S_IRUGO | S_IWUSR; -]]> - - - - and set the write buffer size and the callback - - - -c.text.write = my_proc_write; -]]> - - - - - - For the write callback, you can use - snd_info_get_line() to get a text line, and - snd_info_get_str() to retrieve a string from - the line. Some examples are found in - core/oss/mixer_oss.c, core/oss/and - pcm_oss.c. - - - - For a raw-data proc-file, set the attributes as follows: - - - -content = SNDRV_INFO_CONTENT_DATA; - entry->private_data = chip; - entry->c.ops = &my_file_io_ops; - entry->size = 4096; - entry->mode = S_IFREG | S_IRUGO; -]]> - - - - - - The callback is much more complicated than the text-file - version. You need to use a low-level I/O functions such as - copy_from/to_user() to transfer the - data. - - - - local_max_size) - size = local_max_size - pos; - if (copy_to_user(buf, local_data + pos, size)) - return -EFAULT; - return size; - } -]]> - - - - - - - - - - - - Power Management - - If the chip is supposed to work with suspend/resume - functions, you need to add power-management code to the - driver. The additional code for power-management should be - ifdef'ed with - CONFIG_PM. - - - - If the driver fully supports suspend/resume - that is, the device can be - properly resumed to its state when suspend was called, - you can set the SNDRV_PCM_INFO_RESUME flag - in the pcm info field. Usually, this is possible when the - registers of the chip can be safely saved and restored to - RAM. If this is set, the trigger callback is called with - SNDRV_PCM_TRIGGER_RESUME after the resume - callback completes. - - - - Even if the driver doesn't support PM fully but - partial suspend/resume is still possible, it's still worthy to - implement suspend/resume callbacks. In such a case, applications - would reset the status by calling - snd_pcm_prepare() and restart the stream - appropriately. Hence, you can define suspend/resume callbacks - below but don't set SNDRV_PCM_INFO_RESUME - info flag to the PCM. - - - - Note that the trigger with SUSPEND can always be called when - snd_pcm_suspend_all is called, - regardless of the SNDRV_PCM_INFO_RESUME flag. - The RESUME flag affects only the behavior - of snd_pcm_resume(). - (Thus, in theory, - SNDRV_PCM_TRIGGER_RESUME isn't needed - to be handled in the trigger callback when no - SNDRV_PCM_INFO_RESUME flag is set. But, - it's better to keep it for compatibility reasons.) - - - In the earlier version of ALSA drivers, a common - power-management layer was provided, but it has been removed. - The driver needs to define the suspend/resume hooks according to - the bus the device is connected to. In the case of PCI drivers, the - callbacks look like below: - - - - - - - - - - The scheme of the real suspend job is as follows. - - - Retrieve the card and the chip data. - Call snd_power_change_state() with - SNDRV_CTL_POWER_D3hot to change the - power status. - Call snd_pcm_suspend_all() to suspend the running PCM streams. - If AC97 codecs are used, call - snd_ac97_suspend() for each codec. - Save the register values if necessary. - Stop the hardware if necessary. - Disable the PCI device by calling - pci_disable_device(). Then, call - pci_save_state() at last. - - - - - A typical code would be like: - - - -private_data; - /* (2) */ - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - /* (3) */ - snd_pcm_suspend_all(chip->pcm); - /* (4) */ - snd_ac97_suspend(chip->ac97); - /* (5) */ - snd_mychip_save_registers(chip); - /* (6) */ - snd_mychip_stop_hardware(chip); - /* (7) */ - pci_disable_device(pci); - pci_save_state(pci); - return 0; - } -]]> - - - - - - The scheme of the real resume job is as follows. - - - Retrieve the card and the chip data. - Set up PCI. First, call pci_restore_state(). - Then enable the pci device again by calling pci_enable_device(). - Call pci_set_master() if necessary, too. - Re-initialize the chip. - Restore the saved registers if necessary. - Resume the mixer, e.g. calling - snd_ac97_resume(). - Restart the hardware (if any). - Call snd_power_change_state() with - SNDRV_CTL_POWER_D0 to notify the processes. - - - - - A typical code would be like: - - - -private_data; - /* (2) */ - pci_restore_state(pci); - pci_enable_device(pci); - pci_set_master(pci); - /* (3) */ - snd_mychip_reinit_chip(chip); - /* (4) */ - snd_mychip_restore_registers(chip); - /* (5) */ - snd_ac97_resume(chip->ac97); - /* (6) */ - snd_mychip_restart_chip(chip); - /* (7) */ - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - return 0; - } -]]> - - - - - - As shown in the above, it's better to save registers after - suspending the PCM operations via - snd_pcm_suspend_all() or - snd_pcm_suspend(). It means that the PCM - streams are already stoppped when the register snapshot is - taken. But, remember that you don't have to restart the PCM - stream in the resume callback. It'll be restarted via - trigger call with SNDRV_PCM_TRIGGER_RESUME - when necessary. - - - - OK, we have all callbacks now. Let's set them up. In the - initialization of the card, make sure that you can get the chip - data from the card instance, typically via - private_data field, in case you - created the chip data individually. - - - -private_data = chip; - .... - } -]]> - - - - When you created the chip data with - snd_card_create(), it's anyway accessible - via private_data field. - - - -private_data; - .... - } -]]> - - - - - - - If you need a space to save the registers, allocate the - buffer for it here, too, since it would be fatal - if you cannot allocate a memory in the suspend phase. - The allocated buffer should be released in the corresponding - destructor. - - - - And next, set suspend/resume callbacks to the pci_driver. - - - - - - - - - - - - - - - - Module Parameters - - There are standard module options for ALSA. At least, each - module should have the index, - id and enable - options. - - - - If the module supports multiple cards (usually up to - 8 = SNDRV_CARDS cards), they should be - arrays. The default initial values are defined already as - constants for easier programming: - - - - - - - - - - If the module supports only a single card, they could be single - variables, instead. enable option is not - always necessary in this case, but it would be better to have a - dummy option for compatibility. - - - - The module parameters must be declared with the standard - module_param()(), - module_param_array()() and - MODULE_PARM_DESC() macros. - - - - The typical coding would be like below: - - - - - - - - - - Also, don't forget to define the module description, classes, - license and devices. Especially, the recent modprobe requires to - define the module license as GPL, etc., otherwise the system is - shown as tainted. - - - - - - - - - - - - - - - - How To Put Your Driver Into ALSA Tree -
- General - - So far, you've learned how to write the driver codes. - And you might have a question now: how to put my own - driver into the ALSA driver tree? - Here (finally :) the standard procedure is described briefly. - - - - Suppose that you create a new PCI driver for the card - xyz. The card module name would be - snd-xyz. The new driver is usually put into the alsa-driver - tree, alsa-driver/pci directory in - the case of PCI cards. - Then the driver is evaluated, audited and tested - by developers and users. After a certain time, the driver - will go to the alsa-kernel tree (to the corresponding directory, - such as alsa-kernel/pci) and eventually - will be integrated into the Linux 2.6 tree (the directory would be - linux/sound/pci). - - - - In the following sections, the driver code is supposed - to be put into alsa-driver tree. The two cases are covered: - a driver consisting of a single source file and one consisting - of several source files. - -
- -
- Driver with A Single Source File - - - - - Modify alsa-driver/pci/Makefile - - - - Suppose you have a file xyz.c. Add the following - two lines - - - - - - - - - - - Create the Kconfig entry - - - - Add the new entry of Kconfig for your xyz driver. - - - - - - - the line, select SND_PCM, specifies that the driver xyz supports - PCM. In addition to SND_PCM, the following components are - supported for select command: - SND_RAWMIDI, SND_TIMER, SND_HWDEP, SND_MPU401_UART, - SND_OPL3_LIB, SND_OPL4_LIB, SND_VX_LIB, SND_AC97_CODEC. - Add the select command for each supported component. - - - - Note that some selections imply the lowlevel selections. - For example, PCM includes TIMER, MPU401_UART includes RAWMIDI, - AC97_CODEC includes PCM, and OPL3_LIB includes HWDEP. - You don't need to give the lowlevel selections again. - - - - For the details of Kconfig script, refer to the kbuild - documentation. - - - - - - - Run cvscompile script to re-generate the configure script and - build the whole stuff again. - - - - -
- -
- Drivers with Several Source Files - - Suppose that the driver snd-xyz have several source files. - They are located in the new subdirectory, - pci/xyz. - - - - - Add a new directory (xyz) in - alsa-driver/pci/Makefile as below - - - - - - - - - - - - Under the directory xyz, create a Makefile - - - Sample Makefile for a driver xyz - - - - - - - - - - Create the Kconfig entry - - - - This procedure is as same as in the last section. - - - - - - Run cvscompile script to re-generate the configure script and - build the whole stuff again. - - - - -
- -
- - - - - - Useful Functions - -
- <function>snd_printk()</function> and friends - - ALSA provides a verbose version of the - printk() function. If a kernel config - CONFIG_SND_VERBOSE_PRINTK is set, this - function prints the given message together with the file name - and the line of the caller. The KERN_XXX - prefix is processed as - well as the original printk() does, so it's - recommended to add this prefix, e.g. - - - - - - - - - - There are also printk()'s for - debugging. snd_printd() can be used for - general debugging purposes. If - CONFIG_SND_DEBUG is set, this function is - compiled, and works just like - snd_printk(). If the ALSA is compiled - without the debugging flag, it's ignored. - - - - snd_printdd() is compiled in only when - CONFIG_SND_DEBUG_VERBOSE is set. Please note - that CONFIG_SND_DEBUG_VERBOSE is not set as default - even if you configure the alsa-driver with - option. You need to give - explicitly option instead. - -
- -
- <function>snd_BUG()</function> - - It shows the BUG? message and - stack trace as well as snd_BUG_ON at the point. - It's useful to show that a fatal error happens there. - - - When no debug flag is set, this macro is ignored. - -
- -
- <function>snd_BUG_ON()</function> - - snd_BUG_ON() macro is similar with - WARN_ON() macro. For example, - - - - - - - - or it can be used as the condition, - - - - - - - - - - The macro takes an conditional expression to evaluate. - When CONFIG_SND_DEBUG, is set, the - expression is actually evaluated. If it's non-zero, it shows - the warning message such as - BUG? (xxx) - normally followed by stack trace. It returns the evaluated - value. - When no CONFIG_SND_DEBUG is set, this - macro always returns zero. - - -
- -
- - - - - - - Acknowledgments - - I would like to thank Phil Kerr for his help for improvement and - corrections of this document. - - - Kevin Conder reformatted the original plain-text to the - DocBook format. - - - Giuliano Pochini corrected typos and contributed the example codes - in the hardware constraints section. - - -
-- cgit v1.2.3-59-g8ed1b From 0bcd783c1f0396b68410fdb41fbe196fbc1947af Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 4 Mar 2009 14:55:30 -0600 Subject: powerpc: add fsl,fifo-depth property to Freescale SSI device nodes The Freescale Serial Synchronous Interface (SSI) is an audio device present on some Freescale SOCs. Various implementations of the SSI have a different transmit and receive FIFO depth, but are otherwise identical. To support these variations, add a new property fsl,fifo-depth to the SSI node that specifies the depth of the FIFOs. Also update the MPC8610 HPCD device tree with this property. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- Documentation/powerpc/dts-bindings/fsl/ssi.txt | 2 ++ arch/powerpc/boot/dts/mpc8610_hpcd.dts | 2 ++ 2 files changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/powerpc/dts-bindings/fsl/ssi.txt b/Documentation/powerpc/dts-bindings/fsl/ssi.txt index a2d963998a65..731332288931 100644 --- a/Documentation/powerpc/dts-bindings/fsl/ssi.txt +++ b/Documentation/powerpc/dts-bindings/fsl/ssi.txt @@ -30,6 +30,8 @@ Required properties: - fsl,capture-dma: phandle to a node for the DMA channel to use for capture (recording) of audio. This is typically dictated by SOC design. See the notes below. +- fsl,fifo-depth: the number of elements in the transmit and receive FIFOs. + This number is the maximum allowed value for SFCSR[TFWM0]. Optional properties: - codec-handle : phandle to a 'codec' node that defines an audio diff --git a/arch/powerpc/boot/dts/mpc8610_hpcd.dts b/arch/powerpc/boot/dts/mpc8610_hpcd.dts index f724d72c7b92..1bd3ebe11437 100644 --- a/arch/powerpc/boot/dts/mpc8610_hpcd.dts +++ b/arch/powerpc/boot/dts/mpc8610_hpcd.dts @@ -217,6 +217,7 @@ codec-handle = <&cs4270>; fsl,playback-dma = <&dma00>; fsl,capture-dma = <&dma01>; + fsl,fifo-depth = <8>; }; ssi@16100 { @@ -225,6 +226,7 @@ reg = <0x16100 0x100>; interrupt-parent = <&mpic>; interrupts = <63 2>; + fsl,fifo-depth = <8>; }; dma@21300 { -- cgit v1.2.3-59-g8ed1b From 6dfc0d2c4b9a5455c60e0b9ee95bbf22fc516cef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Mar 2009 07:54:20 +0100 Subject: ALSA: hda - Add missing models to documentation Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 80b796e4a80e..f9253ea3c19f 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -344,7 +344,9 @@ STAC92HD71B* dell-m4-1 Dell desktops dell-m4-2 Dell desktops dell-m4-3 Dell desktops - hp-m4 HP dv laptops + hp-m4 HP mini 1000 + hp-dv5 HP dv series + hp-hdx HP HDX series auto BIOS setup (default) STAC92HD73* @@ -361,6 +363,7 @@ STAC92HD83* =========== ref Reference board mic-ref Reference board with power managment for ports + dell-s14 Dell laptop auto BIOS setup (default) STAC9872 -- cgit v1.2.3-59-g8ed1b From 307282c8990c5658604b9fda8a64a9a07079b850 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Mar 2009 18:17:58 +0100 Subject: ALSA: hda - Add model=vaio for STAC9872 Add the default pin config for model=vaio (in case of broken BIOS). Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 3 ++- sound/pci/hda/patch_sigmatel.c | 33 ++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index f9253ea3c19f..8eec05bc079e 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -368,4 +368,5 @@ STAC92HD83* STAC9872 ======== - N/A + vaio VAIO laptop without SPDIF + auto BIOS setup (default) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 72c87aa20bd9..e06fc7decd31 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -155,6 +155,12 @@ enum { STAC_927X_MODELS }; +enum { + STAC_9872_AUTO, + STAC_9872_VAIO, + STAC_9872_MODELS +}; + struct sigmatel_event { hda_nid_t nid; unsigned char type; @@ -5588,6 +5594,25 @@ static hda_nid_t stac9872_mux_nids[] = { 0x15 }; +static unsigned int stac9872_vaio_pin_configs[9] = { + 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030, + 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0, + 0x90a7013e +}; + +static const char *stac9872_models[STAC_9872_MODELS] = { + [STAC_9872_AUTO] = "auto", + [STAC_9872_VAIO] = "vaio", +}; + +static unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = { + [STAC_9872_VAIO] = stac9872_vaio_pin_configs, +}; + +static struct snd_pci_quirk stac9872_cfg_tbl[] = { + {} /* terminator */ +}; + static int patch_stac9872(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -5598,11 +5623,15 @@ static int patch_stac9872(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; -#if 0 /* no model right now */ spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, stac9872_models, stac9872_cfg_tbl); -#endif + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9872, " + "using BIOS defaults\n"); + else + stac92xx_set_config_regs(codec, + stac9872_brd_tbl[spec->board_config]); spec->num_pins = ARRAY_SIZE(stac9872_pin_nids); spec->pin_nids = stac9872_pin_nids; -- cgit v1.2.3-59-g8ed1b From 71969fd9e2c523d22bf1742eb31f1562247710eb Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Sun, 25 Jan 2009 16:50:02 +0200 Subject: [SCSI] major.h: char-major number for OSD device driver Allocate major 260 for osd. Signed-off-by: Boaz Harrosh CC: Torben Mathiasen Signed-off-by: James Bottomley --- Documentation/devices.txt | 6 ++++++ include/linux/major.h | 1 + 2 files changed, 7 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devices.txt b/Documentation/devices.txt index 2be08240ee80..62254d4510c6 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -3145,6 +3145,12 @@ Your cooperation is appreciated. 1 = /dev/blockrom1 Second ROM card's translation layer interface ... +260 char OSD (Object-based-device) SCSI Device + 0 = /dev/osd0 First OSD Device + 1 = /dev/osd1 Second OSD Device + ... + 255 = /dev/osd255 256th OSD Device + **** ADDITIONAL /dev DIRECTORY ENTRIES This section details additional entries that should or may exist in diff --git a/include/linux/major.h b/include/linux/major.h index 88249452b935..058ec15dd060 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -171,5 +171,6 @@ #define VIOTAPE_MAJOR 230 #define BLOCK_EXT_MAJOR 259 +#define SCSI_OSD_MAJOR 260 /* open-osd's OSD scsi device */ #endif -- cgit v1.2.3-59-g8ed1b From 78e0c621deca08e5f802383dbe75fd20b258ea4e Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Sun, 25 Jan 2009 17:21:40 +0200 Subject: [SCSI] osd: Documentation for OSD library Add osd.txt to Documentation/scsi/ Signed-off-by: Boaz Harrosh Reviewed-by: Benny Halevy Signed-off-by: James Bottomley --- Documentation/scsi/osd.txt | 198 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 Documentation/scsi/osd.txt (limited to 'Documentation') diff --git a/Documentation/scsi/osd.txt b/Documentation/scsi/osd.txt new file mode 100644 index 000000000000..da162f7fd5f5 --- /dev/null +++ b/Documentation/scsi/osd.txt @@ -0,0 +1,198 @@ +The OSD Standard +================ +OSD (Object-Based Storage Device) is a T10 SCSI command set that is designed +to provide efficient operation of input/output logical units that manage the +allocation, placement, and accessing of variable-size data-storage containers, +called objects. Objects are intended to contain operating system and application +constructs. Each object has associated attributes attached to it, which are +integral part of the object and provide metadata about the object. The standard +defines some common obligatory attributes, but user attributes can be added as +needed. + +See: http://www.t10.org/ftp/t10/drafts/osd2/ for the latest draft for OSD 2 +or search the web for "OSD SCSI" + +OSD in the Linux Kernel +======================= +osd-initiator: + The main component of OSD in Kernel is the osd-initiator library. Its main +user is intended to be the pNFS-over-objects layout driver, which uses objects +as its back-end data storage. Other clients are the other osd parts listed below. + +osd-uld: + This is a SCSI ULD that registers for OSD type devices and provides a testing +platform, both for the in-kernel initiator as well as connected targets. It +currently has no useful user-mode API, though it could have if need be. + +exofs: + Is an OSD based Linux file system. It uses the osd-initiator and osd-uld, +to export a usable file system for users. +See Documentation/filesystems/exofs.txt for more details + +osd target: + There are no current plans for an OSD target implementation in kernel. For all +needs, a user-mode target that is based on the scsi tgt target framework is +available from Ohio Supercomputer Center (OSC) at: +http://www.open-osd.org/bin/view/Main/OscOsdProject +There are several other target implementations. See http://open-osd.org for more +links. + +Files and Folders +================= +This is the complete list of files included in this work: +include/scsi/ + osd_initiator.h Main API for the initiator library + osd_types.h Common OSD types + osd_sec.h Security Manager API + osd_protocol.h Wire definitions of the OSD standard protocol + osd_attributes.h Wire definitions of OSD attributes + +drivers/scsi/osd/ + osd_initiator.c OSD-Initiator library implementation + osd_uld.c The OSD scsi ULD + osd_ktest.{h,c} In-kernel test suite (called by osd_uld) + osd_debug.h Some printk macros + Makefile For both in-tree and out-of-tree compilation + Kconfig Enables inclusion of the different pieces + osd_test.c User-mode application to call the kernel tests + +The OSD-Initiator Library +========================= +osd_initiator is a low level implementation of an osd initiator encoder. +But even though, it should be intuitive and easy to use. Perhaps over time an +higher lever will form that automates some of the more common recipes. + +init/fini: +- osd_dev_init() associates a scsi_device with an osd_dev structure + and initializes some global pools. This should be done once per scsi_device + (OSD LUN). The osd_dev structure is needed for calling osd_start_request(). + +- osd_dev_fini() cleans up before a osd_dev/scsi_device destruction. + +OSD commands encoding, execution, and decoding of results: + +struct osd_request's is used to iteratively encode an OSD command and carry +its state throughout execution. Each request goes through these stages: + +a. osd_start_request() allocates the request. + +b. Any of the osd_req_* methods is used to encode a request of the specified + type. + +c. osd_req_add_{get,set}_attr_* may be called to add get/set attributes to the + CDB. "List" or "Page" mode can be used exclusively. The attribute-list API + can be called multiple times on the same request. However, only one + attribute-page can be read, as mandated by the OSD standard. + +d. osd_finalize_request() computes offsets into the data-in and data-out buffers + and signs the request using the provided capability key and integrity- + check parameters. + +e. osd_execute_request() may be called to execute the request via the block + layer and wait for its completion. The request can be executed + asynchronously by calling the block layer API directly. + +f. After execution, osd_req_decode_sense() can be called to decode the request's + sense information. + +g. osd_req_decode_get_attr() may be called to retrieve osd_add_get_attr_list() + values. + +h. osd_end_request() must be called to deallocate the request and any resource + associated with it. Note that osd_end_request cleans up the request at any + stage and it must always be called after a successful osd_start_request(). + +osd_request's structure: + +The OSD standard defines a complex structure of IO segments pointed to by +members in the CDB. Up to 3 segments can be deployed in the IN-Buffer and up to +4 in the OUT-Buffer. The ASCII illustration below depicts a secure-read with +associated get+set of attributes-lists. Other combinations very on the same +basic theme. From no-segments-used up to all-segments-used. + +|________OSD-CDB__________| +| | +|read_len (offset=0) -|---------\ +| | | +|get_attrs_list_length | | +|get_attrs_list_offset -|----\ | +| | | | +|retrieved_attrs_alloc_len| | | +|retrieved_attrs_offset -|----|----|-\ +| | | | | +|set_attrs_list_length | | | | +|set_attrs_list_offset -|-\ | | | +| | | | | | +|in_data_integ_offset -|-|--|----|-|-\ +|out_data_integ_offset -|-|--|--\ | | | +\_________________________/ | | | | | | + | | | | | | +|_______OUT-BUFFER________| | | | | | | +| Set attr list | + +More up-to-date information can be found on: +http://open-osd.org + +Boaz Harrosh +Benny Halevy + +References +========== +Weber, R., "SCSI Object-Based Storage Device Commands", +T10/1355-D ANSI/INCITS 400-2004, +http://www.t10.org/ftp/t10/drafts/osd/osd-r10.pdf + +Weber, R., "SCSI Object-Based Storage Device Commands -2 (OSD-2)" +T10/1729-D, Working Draft, rev. 3 +http://www.t10.org/ftp/t10/drafts/osd2/osd2r03.pdf -- cgit v1.2.3-59-g8ed1b From a9d0a1a38352c4fb8946e73b3e42ba4ada29e733 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 3 Mar 2009 16:58:16 +0100 Subject: genirq: add doc to struct irqaction Impact: documentation struct irqaction is not documented. Add kernel doc comments and add interrupt.h to the genirq docbook. Signed-off-by: Thomas Gleixner --- Documentation/DocBook/genericirq.tmpl | 1 + include/linux/interrupt.h | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/genericirq.tmpl b/Documentation/DocBook/genericirq.tmpl index 3a882d9a90a9..c671a0168096 100644 --- a/Documentation/DocBook/genericirq.tmpl +++ b/Documentation/DocBook/genericirq.tmpl @@ -440,6 +440,7 @@ desc->chip->end(); used in the generic IRQ layer.
!Iinclude/linux/irq.h +!Iinclude/linux/interrupt.h diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 468e3a25a4a1..91658d076598 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -61,6 +61,17 @@ typedef irqreturn_t (*irq_handler_t)(int, void *); +/** + * struct irqaction - per interrupt action descriptor + * @handler: interrupt handler function + * @flags: flags (see IRQF_* above) + * @mask: no comment as it is useless and about to be removed + * @name: name of the device + * @dev_id: cookie to identify the device + * @next: pointer to the next irqaction for shared interrupts + * @irq: interrupt number + * @dir: pointer to the proc/irq/NN/name entry + */ struct irqaction { irq_handler_t handler; unsigned long flags; -- cgit v1.2.3-59-g8ed1b From 0e57aa11abb15b70db53d1f95ae70b3c980ac885 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 13 Mar 2009 14:34:05 +0100 Subject: genirq: deprecate __do_IRQ Two years migration time is enough. Remove the compability cruft. Add the deprecated warning in kernel/irq/handle.c because marking __do_IRQ itself is way too noisy. Signed-off-by: Thomas Gleixner --- Documentation/feature-removal-schedule.txt | 8 ++++++++ kernel/irq/handle.c | 5 +++++ 2 files changed, 13 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 20d3b94703a4..63b4550411be 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -344,3 +344,11 @@ Why: See commits 129f8ae9b1b5be94517da76009ea956e89104ce8 and Removal is subject to fixing any remaining bugs in ACPI which may cause the thermal throttling not to happen at the right time. Who: Dave Jones , Matthew Garrett + +----------------------------- + +What: __do_IRQ all in one fits nothing interrupt handler +When: 2.6.32 +Why: __do_IRQ was kept for easy migration to the type flow handlers. + More than two years of migration time is enough. +Who: Thomas Gleixner diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index a2ee682bca2e..6661704140c7 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -349,6 +349,11 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) } #ifndef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ + +#ifdef CONFIG_ENABLE_WARN_DEPRECATED +# warning __do_IRQ is deprecated. Please convert to proper flow handlers +#endif + /** * __do_IRQ - original all in one highlevel IRQ handler * @irq: the interrupt number -- cgit v1.2.3-59-g8ed1b From cb065c06b6cc615a58860d619d7fa7952cd6a18b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 13 Mar 2009 14:40:27 +0100 Subject: genirq: deprecate obsolete typedefs and defines More than two years is enough migration time. Remove the compability cruft. Signed-off-by: Thomas Gleixner --- Documentation/feature-removal-schedule.txt | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 63b4550411be..9aa1dda5d38c 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -352,3 +352,12 @@ When: 2.6.32 Why: __do_IRQ was kept for easy migration to the type flow handlers. More than two years of migration time is enough. Who: Thomas Gleixner + +----------------------------- + +What: obsolete generic irq defines and typedefs +When: 2.6.30 +Why: The defines and typedefs (hw_interrupt_type, no_irq_type, irq_desc_t) + have been kept around for migration reasons. After more than two years + it's time to remove them finally +Who: Thomas Gleixner -- cgit v1.2.3-59-g8ed1b From 09e1c061484005aa26264c3f82f2c83a273c4094 Mon Sep 17 00:00:00 2001 From: PJ Waskiewicz Date: Fri, 13 Mar 2009 22:15:54 +0000 Subject: ixgbe: Add documentation for the driver Documentation for the ixgbe driver in the kernel docs area is missing. This adds that documentation. Signed-off-by: Peter P Waskiewicz Jr Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- Documentation/networking/ixgbe.txt | 199 +++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 Documentation/networking/ixgbe.txt (limited to 'Documentation') diff --git a/Documentation/networking/ixgbe.txt b/Documentation/networking/ixgbe.txt new file mode 100644 index 000000000000..eeb68685c788 --- /dev/null +++ b/Documentation/networking/ixgbe.txt @@ -0,0 +1,199 @@ +Linux Base Driver for 10 Gigabit PCI Express Intel(R) Network Connection +======================================================================== + +March 10, 2009 + + +Contents +======== + +- In This Release +- Identifying Your Adapter +- Building and Installation +- Additional Configurations +- Support + + + +In This Release +=============== + +This file describes the ixgbe Linux Base Driver for the 10 Gigabit PCI +Express Intel(R) Network Connection. This driver includes support for +Itanium(R)2-based systems. + +For questions related to hardware requirements, refer to the documentation +supplied with your 10 Gigabit adapter. All hardware requirements listed apply +to use with Linux. + +The following features are available in this kernel: + - Native VLANs + - Channel Bonding (teaming) + - SNMP + - Generic Receive Offload + - Data Center Bridging + +Channel Bonding documentation can be found in the Linux kernel source: +/Documentation/networking/bonding.txt + +Ethtool, lspci, and ifconfig can be used to display device and driver +specific information. + + +Identifying Your Adapter +======================== + +This driver supports devices based on the 82598 controller and the 82599 +controller. + +For specific information on identifying which adapter you have, please visit: + + http://support.intel.com/support/network/sb/CS-008441.htm + + +Building and Installation +========================= + +select m for "Intel(R) 10GbE PCI Express adapters support" located at: + Location: + -> Device Drivers + -> Network device support (NETDEVICES [=y]) + -> Ethernet (10000 Mbit) (NETDEV_10000 [=y]) + +1. make modules & make modules_install + +2. Load the module: + +# modprobe ixgbe + + The insmod command can be used if the full + path to the driver module is specified. For example: + + insmod /lib/modules//kernel/drivers/net/ixgbe/ixgbe.ko + + With 2.6 based kernels also make sure that older ixgbe drivers are + removed from the kernel, before loading the new module: + + rmmod ixgbe; modprobe ixgbe + +3. Assign an IP address to the interface by entering the following, where + x is the interface number: + + ifconfig ethx + +4. Verify that the interface works. Enter the following, where + is the IP address for another machine on the same subnet as the interface + that is being tested: + + ping + + +Additional Configurations +========================= + + Viewing Link Messages + --------------------- + Link messages will not be displayed to the console if the distribution is + restricting system messages. In order to see network driver link messages on + your console, set dmesg to eight by entering the following: + + dmesg -n 8 + + NOTE: This setting is not saved across reboots. + + + Jumbo Frames + ------------ + The driver supports Jumbo Frames for all adapters. Jumbo Frames support is + enabled by changing the MTU to a value larger than the default of 1500. + The maximum value for the MTU is 16110. Use the ifconfig command to + increase the MTU size. For example: + + ifconfig ethx mtu 9000 up + + The maximum MTU setting for Jumbo Frames is 16110. This value coincides + with the maximum Jumbo Frames size of 16128. + + Generic Receive Offload, aka GRO + -------------------------------- + The driver supports the in-kernel software implementation of GRO. GRO has + shown that by coalescing Rx traffic into larger chunks of data, CPU + utilization can be significantly reduced when under large Rx load. GRO is an + evolution of the previously-used LRO interface. GRO is able to coalesce + other protocols besides TCP. It's also safe to use with configurations that + are problematic for LRO, namely bridging and iSCSI. + + GRO is enabled by default in the driver. Future versions of ethtool will + support disabling and re-enabling GRO on the fly. + + + Data Center Bridging, aka DCB + ----------------------------- + + DCB is a configuration Quality of Service implementation in hardware. + It uses the VLAN priority tag (802.1p) to filter traffic. That means + that there are 8 different priorities that traffic can be filtered into. + It also enables priority flow control which can limit or eliminate the + number of dropped packets during network stress. Bandwidth can be + allocated to each of these priorities, which is enforced at the hardware + level. + + To enable DCB support in ixgbe, you must enable the DCB netlink layer to + allow the userspace tools (see below) to communicate with the driver. + This can be found in the kernel configuration here: + + -> Networking support + -> Networking options + -> Data Center Bridging support + + Once this is selected, DCB support must be selected for ixgbe. This can + be found here: + + -> Device Drivers + -> Network device support (NETDEVICES [=y]) + -> Ethernet (10000 Mbit) (NETDEV_10000 [=y]) + -> Intel(R) 10GbE PCI Express adapters support + -> Data Center Bridging (DCB) Support + + After these options are selected, you must rebuild your kernel and your + modules. + + In order to use DCB, userspace tools must be downloaded and installed. + The dcbd tools can be found at: + + http://e1000.sf.net + + + Ethtool + ------- + The driver utilizes the ethtool interface for driver configuration and + diagnostics, as well as displaying statistical information. Ethtool + version 3.0 or later is required for this functionality. + + The latest release of ethtool can be found from + http://sourceforge.net/projects/gkernel. + + + NAPI + ---- + + NAPI (Rx polling mode) is supported in the ixgbe driver. NAPI is enabled + by default in the driver. + + See www.cyberus.ca/~hadi/usenix-paper.tgz for more information on NAPI. + + +Support +======= + +For general information, go to the Intel support website at: + + http://support.intel.com + +or the Intel Wired Networking project hosted by Sourceforge at: + + http://e1000.sourceforge.net + +If an issue is identified with the released source code on the supported +kernel with a supported adapter, email the specific information related +to the issue to e1000-devel@lists.sf.net -- cgit v1.2.3-59-g8ed1b From 5f0fbf9ecaf354fa4bbf266fffdea2ea3d14a0ed Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 16 Sep 2008 13:05:53 -0400 Subject: [ARM] fixmap support This is the minimum fixmap interface expected to be implemented by architectures supporting highmem. We have a second level page table already allocated and covering 0xfff00000-0xffffffff because the exception vector page is located at 0xffff0000, and various cache tricks already use some entries above 0xffff0000. Therefore the PTEs covering 0xfff00000-0xfffeffff are free to be used. However the XScale cache flushing code already uses virtual addresses between 0xfffe0000 and 0xfffeffff. So this reserves the 0xfff00000-0xfffdffff range for fixmap stuff. The Documentation/arm/memory.txt information is updated accordingly, including the information about the actual top of DMA memory mapping region which didn't match the code. Signed-off-by: Nicolas Pitre --- Documentation/arm/memory.txt | 9 ++++++++- arch/arm/include/asm/fixmap.h | 41 +++++++++++++++++++++++++++++++++++++++++ arch/arm/mm/mm.h | 3 +-- 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 arch/arm/include/asm/fixmap.h (limited to 'Documentation') diff --git a/Documentation/arm/memory.txt b/Documentation/arm/memory.txt index dc6045577a8b..43cb1004d35f 100644 --- a/Documentation/arm/memory.txt +++ b/Documentation/arm/memory.txt @@ -29,7 +29,14 @@ ffff0000 ffff0fff CPU vector page. CPU supports vector relocation (control register V bit.) -ffc00000 fffeffff DMA memory mapping region. Memory returned +fffe0000 fffeffff XScale cache flush area. This is used + in proc-xscale.S to flush the whole data + cache. Free for other usage on non-XScale. + +fff00000 fffdffff Fixmap mapping region. Addresses provided + by fix_to_virt() will be located here. + +ffc00000 ffefffff DMA memory mapping region. Memory returned by the dma_alloc_xxx functions will be dynamically mapped here. diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h new file mode 100644 index 000000000000..bbae919bceb4 --- /dev/null +++ b/arch/arm/include/asm/fixmap.h @@ -0,0 +1,41 @@ +#ifndef _ASM_FIXMAP_H +#define _ASM_FIXMAP_H + +/* + * Nothing too fancy for now. + * + * On ARM we already have well known fixed virtual addresses imposed by + * the architecture such as the vector page which is located at 0xffff0000, + * therefore a second level page table is already allocated covering + * 0xfff00000 upwards. + * + * The cache flushing code in proc-xscale.S uses the virtual area between + * 0xfffe0000 and 0xfffeffff. + */ + +#define FIXADDR_START 0xfff00000UL +#define FIXADDR_TOP 0xfffe0000UL +#define FIXADDR_SIZE (FIXADDR_TOP - FIXADDR_START) + +#define FIX_KMAP_BEGIN 0 +#define FIX_KMAP_END (FIXADDR_SIZE >> PAGE_SHIFT) + +#define __fix_to_virt(x) (FIXADDR_START + ((x) << PAGE_SHIFT)) +#define __virt_to_fix(x) (((x) - FIXADDR_START) >> PAGE_SHIFT) + +extern void __this_fixmap_does_not_exist(void); + +static inline unsigned long fix_to_virt(const unsigned int idx) +{ + if (idx >= FIX_KMAP_END) + __this_fixmap_does_not_exist(); + return __fix_to_virt(idx); +} + +static inline unsigned int virt_to_fix(const unsigned long vaddr) +{ + BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START); + return __virt_to_fix(vaddr); +} + +#endif diff --git a/arch/arm/mm/mm.h b/arch/arm/mm/mm.h index 95bbe112965e..c4f6f05198e0 100644 --- a/arch/arm/mm/mm.h +++ b/arch/arm/mm/mm.h @@ -1,7 +1,6 @@ -/* the upper-most page table pointer */ - #ifdef CONFIG_MMU +/* the upper-most page table pointer */ extern pmd_t *top_pmd; #define TOP_PTE(x) pte_offset_kernel(top_pmd, x) -- cgit v1.2.3-59-g8ed1b From 76398425bb06b07cc3a3b1ce169c67dc9d6874ed Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sun, 1 Feb 2009 14:26:59 -0700 Subject: Move FASYNC bit handling to f_op->fasync() Removing the BKL from FASYNC handling ran into the challenge of keeping the setting of the FASYNC bit in filp->f_flags atomic with regard to calls to the underlying fasync() function. Andi Kleen suggested moving the handling of that bit into fasync(); this patch does exactly that. As a result, we have a couple of internal API changes: fasync() must now manage the FASYNC bit, and it will be called without the BKL held. As it happens, every fasync() implementation in the kernel with one exception calls fasync_helper(). So, if we make fasync_helper() set the FASYNC bit, we can avoid making any changes to the other fasync() functions - as long as those functions, themselves, have proper locking. Most fasync() implementations do nothing but call fasync_helper() - which has its own lock - so they are easily verified as correct. The BKL had already been pushed down into the rest. The networking code has its own version of fasync_helper(), so that code has been augmented with explicit FASYNC bit handling. Cc: Al Viro Cc: David Miller Reviewed-by: Christoph Hellwig Signed-off-by: Jonathan Corbet --- Documentation/filesystems/Locking | 7 +++++-- fs/fcntl.c | 29 ++++++++++++++++------------- fs/ioctl.c | 13 +------------ net/socket.c | 7 +++++++ 4 files changed, 29 insertions(+), 27 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index ec6a9392a173..4e78ce677843 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -437,8 +437,11 @@ grab BKL for cases when we close a file that had been opened r/w, but that can and should be done using the internal locking with smaller critical areas). Current worst offender is ext2_get_block()... -->fasync() is a mess. This area needs a big cleanup and that will probably -affect locking. +->fasync() is called without BKL protection, and is responsible for +maintaining the FASYNC bit in filp->f_flags. Most instances call +fasync_helper(), which does that maintenance, so it's not normally +something one needs to worry about. Return values > 0 will be mapped to +zero in the VFS layer. ->readdir() and ->ioctl() on directories must be changed. Ideally we would move ->readdir() to inode_operations and use a separate method for directory diff --git a/fs/fcntl.c b/fs/fcntl.c index 04df8570a2d2..431bb6459273 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -141,7 +141,7 @@ SYSCALL_DEFINE1(dup, unsigned int, fildes) return ret; } -#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT | O_NOATIME) +#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) static int setfl(int fd, struct file * filp, unsigned long arg) { @@ -177,23 +177,19 @@ static int setfl(int fd, struct file * filp, unsigned long arg) return error; /* - * We still need a lock here for now to keep multiple FASYNC calls - * from racing with each other. + * ->fasync() is responsible for setting the FASYNC bit. */ - lock_kernel(); - if ((arg ^ filp->f_flags) & FASYNC) { - if (filp->f_op && filp->f_op->fasync) { - error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); - if (error < 0) - goto out; - } + if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op && + filp->f_op->fasync) { + error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); + if (error < 0) + goto out; } - spin_lock(&filp->f_lock); filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK); spin_unlock(&filp->f_lock); + out: - unlock_kernel(); return error; } @@ -518,7 +514,7 @@ static DEFINE_RWLOCK(fasync_lock); static struct kmem_cache *fasync_cache __read_mostly; /* - * fasync_helper() is used by some character device drivers (mainly mice) + * fasync_helper() is used by almost all character device drivers * to set up the fasync queue. It returns negative on error, 0 if it did * no changes and positive if it added/deleted the entry. */ @@ -557,6 +553,13 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap result = 1; } out: + /* Fix up FASYNC bit while still holding fasync_lock */ + spin_lock(&filp->f_lock); + if (on) + filp->f_flags |= FASYNC; + else + filp->f_flags &= ~FASYNC; + spin_unlock(&filp->f_lock); write_unlock_irq(&fasync_lock); return result; } diff --git a/fs/ioctl.c b/fs/ioctl.c index 421aab465dab..e8e89edba576 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -427,19 +427,11 @@ static int ioctl_fioasync(unsigned int fd, struct file *filp, /* Did FASYNC state change ? */ if ((flag ^ filp->f_flags) & FASYNC) { if (filp->f_op && filp->f_op->fasync) + /* fasync() adjusts filp->f_flags */ error = filp->f_op->fasync(fd, filp, on); else error = -ENOTTY; } - if (error) - return error; - - spin_lock(&filp->f_lock); - if (on) - filp->f_flags |= FASYNC; - else - filp->f_flags &= ~FASYNC; - spin_unlock(&filp->f_lock); return error; } @@ -507,10 +499,7 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, break; case FIOASYNC: - /* BKL needed to avoid races tweaking f_flags */ - lock_kernel(); error = ioctl_fioasync(fd, filp, argp); - unlock_kernel(); break; case FIOQSIZE: diff --git a/net/socket.c b/net/socket.c index 35dd7371752a..0f75746ab06e 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1030,6 +1030,13 @@ static int sock_fasync(int fd, struct file *filp, int on) lock_sock(sk); + spin_lock(&filp->f_lock); + if (on) + filp->f_flags |= FASYNC; + else + filp->f_flags &= ~FASYNC; + spin_unlock(&filp->f_lock); + prev = &(sock->fasync_list); for (fa = *prev; fa != NULL; prev = &fa->fa_next, fa = *prev) -- cgit v1.2.3-59-g8ed1b From 187f9c3f05373df4f7cbae2e656450acdbba7558 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 9 Jan 2009 16:28:07 +0100 Subject: dma-debug: Documentation update Impact: add documentation about DMA-API debugging to DMA-API.txt Signed-off-by: Joerg Roedel --- Documentation/DMA-API.txt | 106 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index 2a3fcc55e981..d9aa43d78bcc 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -609,3 +609,109 @@ size is the size (and should be a page-sized multiple). The return value will be either a pointer to the processor virtual address of the memory, or an error (via PTR_ERR()) if any part of the region is occupied. + +Part III - Debug drivers use of the DMA-API +------------------------------------------- + +The DMA-API as described above as some constraints. DMA addresses must be +released with the corresponding function with the same size for example. With +the advent of hardware IOMMUs it becomes more and more important that drivers +do not violate those constraints. In the worst case such a violation can +result in data corruption up to destroyed filesystems. + +To debug drivers and find bugs in the usage of the DMA-API checking code can +be compiled into the kernel which will tell the developer about those +violations. If your architecture supports it you can select the "Enable +debugging of DMA-API usage" option in your kernel configuration. Enabling this +option has a performance impact. Do not enable it in production kernels. + +If you boot the resulting kernel will contain code which does some bookkeeping +about what DMA memory was allocated for which device. If this code detects an +error it prints a warning message with some details into your kernel log. An +example warning message may look like this: + +------------[ cut here ]------------ +WARNING: at /data2/repos/linux-2.6-iommu/lib/dma-debug.c:448 + check_unmap+0x203/0x490() +Hardware name: +forcedeth 0000:00:08.0: DMA-API: device driver frees DMA memory with wrong + function [device address=0x00000000640444be] [size=66 bytes] [mapped as +single] [unmapped as page] +Modules linked in: nfsd exportfs bridge stp llc r8169 +Pid: 0, comm: swapper Tainted: G W 2.6.28-dmatest-09289-g8bb99c0 #1 +Call Trace: + [] warn_slowpath+0xf2/0x130 + [] _spin_unlock+0x10/0x30 + [] usb_hcd_link_urb_to_ep+0x75/0xc0 + [] _spin_unlock_irqrestore+0x12/0x40 + [] ohci_urb_enqueue+0x19f/0x7c0 + [] queue_work+0x56/0x60 + [] enqueue_task_fair+0x20/0x50 + [] usb_hcd_submit_urb+0x379/0xbc0 + [] cpumask_next_and+0x23/0x40 + [] find_busiest_group+0x207/0x8a0 + [] _spin_lock_irqsave+0x1f/0x50 + [] check_unmap+0x203/0x490 + [] debug_dma_unmap_page+0x49/0x50 + [] nv_tx_done_optimized+0xc6/0x2c0 + [] nv_nic_irq_optimized+0x73/0x2b0 + [] handle_IRQ_event+0x34/0x70 + [] handle_edge_irq+0xc9/0x150 + [] do_IRQ+0xcb/0x1c0 + [] ret_from_intr+0x0/0xa + <4>---[ end trace f6435a98e2a38c0e ]--- + +The driver developer can find the driver and the device including a stacktrace +of the DMA-API call which caused this warning. + +Per default only the first error will result in a warning message. All other +errors will only silently counted. This limitation exist to prevent the code +from flooding your kernel log. To support debugging a device driver this can +be disabled via debugfs. See the debugfs interface documentation below for +details. + +The debugfs directory for the DMA-API debugging code is called dma-api/. In +this directory the following files can currently be found: + + dma-api/all_errors This file contains a numeric value. If this + value is not equal to zero the debugging code + will print a warning for every error it finds + into the kernel log. Be carefull with this + option. It can easily flood your logs. + + dma-api/disabled This read-only file contains the character 'Y' + if the debugging code is disabled. This can + happen when it runs out of memory or if it was + disabled at boot time + + dma-api/error_count This file is read-only and shows the total + numbers of errors found. + + dma-api/num_errors The number in this file shows how many + warnings will be printed to the kernel log + before it stops. This number is initialized to + one at system boot and be set by writing into + this file + + dma-api/min_free_entries + This read-only file can be read to get the + minimum number of free dma_debug_entries the + allocator has ever seen. If this value goes + down to zero the code will disable itself + because it is not longer reliable. + + dma-api/num_free_entries + The current number of free dma_debug_entries + in the allocator. + +If you have this code compiled into your kernel it will be enabled by default. +If you want to boot without the bookkeeping anyway you can provide +'dma_debug=off' as a boot parameter. This will disable DMA-API debugging. +Notice that you can not enable it again at runtime. You have to reboot to do +so. + +When the code disables itself at runtime this is most likely because it ran +out of dma_debug_entries. These entries are preallocated at boot. The number +of preallocated entries is defined per architecture. If it is too low for you +boot with 'dma_debug_entries=' to overwrite the +architectural default. -- cgit v1.2.3-59-g8ed1b From 9bdd8d40c8c59435664af6049dabe24b7779b203 Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Wed, 18 Mar 2009 18:22:48 -0700 Subject: ipv6: Fix incorrect disable_ipv6 behavior Fix the behavior of allowing both sysctl and addrconf_dad_failure() to set the disable_ipv6 parameter without any bad side-effects. If DAD fails and accept_dad > 1, we will still set disable_ipv6=1, but then instead of allowing an RA to add an address then immediately fail DAD, we simply don't allow the address to be added in the first place. This also lets the user set this flag and disable all IPv6 addresses on the interface, or on the entire system. Signed-off-by: Brian Haley Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 4 +++- net/ipv6/addrconf.c | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 7185e4c41e59..ec5de02f543f 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1043,7 +1043,9 @@ max_addresses - INTEGER Default: 16 disable_ipv6 - BOOLEAN - Disable IPv6 operation. + Disable IPv6 operation. If accept_dad is set to 2, this value + will be dynamically set to TRUE if DAD fails for the link-local + address. Default: FALSE (enable IPv6 operation) accept_dad - INTEGER diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e83852ab4dc8..717584bad02e 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -590,6 +590,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, { struct inet6_ifaddr *ifa = NULL; struct rt6_info *rt; + struct net *net = dev_net(idev->dev); int hash; int err = 0; int addr_type = ipv6_addr_type(addr); @@ -606,6 +607,11 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, goto out2; } + if (idev->cnf.disable_ipv6 || net->ipv6.devconf_all->disable_ipv6) { + err = -EACCES; + goto out2; + } + write_lock(&addrconf_hash_lock); /* Ignore adding duplicate addresses on an interface */ @@ -1433,6 +1439,11 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp) void addrconf_dad_failure(struct inet6_ifaddr *ifp) { struct inet6_dev *idev = ifp->idev; + + if (net_ratelimit()) + printk(KERN_INFO "%s: IPv6 duplicate address detected!\n", + ifp->idev->dev->name); + if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) { struct in6_addr addr; @@ -1443,11 +1454,12 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) ipv6_addr_equal(&ifp->addr, &addr)) { /* DAD failed for link-local based on MAC address */ idev->cnf.disable_ipv6 = 1; + + printk(KERN_INFO "%s: IPv6 being disabled!\n", + ifp->idev->dev->name); } } - if (net_ratelimit()) - printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name); addrconf_dad_stop(ifp); } @@ -2823,11 +2835,6 @@ static void addrconf_dad_timer(unsigned long data) read_unlock_bh(&idev->lock); goto out; } - if (idev->cnf.accept_dad > 1 && idev->cnf.disable_ipv6) { - read_unlock_bh(&idev->lock); - addrconf_dad_failure(ifp); - return; - } spin_lock_bh(&ifp->lock); if (ifp->probes == 0) { /* -- cgit v1.2.3-59-g8ed1b From 1cc0ca26c57221f5aa0f49a311a8fc83413dfe97 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 14 Jan 2009 10:04:36 -0700 Subject: PCI/x86: document pci=earlydump argument Document the "pci=earlydump" argument. This currently only works on x86. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 54f21a5c262b..c7c441e7930e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1659,6 +1659,8 @@ and is between 256 and 4096 characters. It is defined in the file See also Documentation/blockdev/paride.txt. pci=option[,option...] [PCI] various PCI subsystem options: + earlydump [X86] dump PCI config space before the kernel + changes anything off [X86] don't probe for the PCI bus bios [X86-32] force use of PCI BIOS, don't access the hardware directly. Use this if your machine -- cgit v1.2.3-59-g8ed1b From 0994375e9614f78657031e04e30019b9cdb62795 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Mon, 23 Feb 2009 21:52:23 -0800 Subject: PCI: add remove_id sysfs entry This adds a remove_id sysfs entry to allow users of new_id to later remove the added dynid. One use case is management tools that want to dynamically bind/unbind devices to pci-stub driver while devices are assigned to KVM guests. Rather than having to track which driver was originally bound to the driver, a mangement tool can simply: Guest uses device Signed-off-by: Chris Wright Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 16 +++++++ drivers/pci/pci-driver.c | 80 ++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index e638e15a8895..3d29793a0ea2 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -41,6 +41,22 @@ Description: for the device and attempt to bind to it. For example: # echo "8086 10f5" > /sys/bus/pci/drivers/foo/new_id +What: /sys/bus/pci/drivers/.../remove_id +Date: February 2009 +Contact: Chris Wright +Description: + Writing a device ID to this file will remove an ID + that was dynamically added via the new_id sysfs entry. + The format for the device ID is: + VVVV DDDD SVVV SDDD CCCC MMMM. That is Vendor ID, Device + ID, Subsystem Vendor ID, Subsystem Device ID, Class, + and Class Mask. The Vendor ID and Device ID fields are + required, the rest are optional. After successfully + removing an ID, the driver will no longer support the + device. This is useful to ensure auto probing won't + match the driver to the device. For example: + # echo "8086 10f5" > /sys/bus/pci/drivers/foo/remove_id + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 93eac1423585..87a5ddbb324f 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -99,6 +99,52 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) } static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); +/** + * store_remove_id - remove a PCI device ID from this driver + * @driver: target device driver + * @buf: buffer for scanning device ID data + * @count: input size + * + * Removes a dynamic pci device ID to this driver. + */ +static ssize_t +store_remove_id(struct device_driver *driver, const char *buf, size_t count) +{ + struct pci_dynid *dynid, *n; + struct pci_driver *pdrv = to_pci_driver(driver); + __u32 vendor, device, subvendor = PCI_ANY_ID, + subdevice = PCI_ANY_ID, class = 0, class_mask = 0; + int fields = 0; + int retval = -ENODEV; + + fields = sscanf(buf, "%x %x %x %x %x %x", + &vendor, &device, &subvendor, &subdevice, + &class, &class_mask); + if (fields < 2) + return -EINVAL; + + spin_lock(&pdrv->dynids.lock); + list_for_each_entry_safe(dynid, n, &pdrv->dynids.list, node) { + struct pci_device_id *id = &dynid->id; + if ((id->vendor == vendor) && + (id->device == device) && + (subvendor == PCI_ANY_ID || id->subvendor == subvendor) && + (subdevice == PCI_ANY_ID || id->subdevice == subdevice) && + !((id->class ^ class) & class_mask)) { + list_del(&dynid->node); + kfree(dynid); + retval = 0; + break; + } + } + spin_unlock(&pdrv->dynids.lock); + + if (retval) + return retval; + return count; +} +static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); + static void pci_free_dynids(struct pci_driver *drv) { @@ -125,6 +171,20 @@ static void pci_remove_newid_file(struct pci_driver *drv) { driver_remove_file(&drv->driver, &driver_attr_new_id); } + +static int +pci_create_removeid_file(struct pci_driver *drv) +{ + int error = 0; + if (drv->probe != NULL) + error = driver_create_file(&drv->driver,&driver_attr_remove_id); + return error; +} + +static void pci_remove_removeid_file(struct pci_driver *drv) +{ + driver_remove_file(&drv->driver, &driver_attr_remove_id); +} #else /* !CONFIG_HOTPLUG */ static inline void pci_free_dynids(struct pci_driver *drv) {} static inline int pci_create_newid_file(struct pci_driver *drv) @@ -132,6 +192,11 @@ static inline int pci_create_newid_file(struct pci_driver *drv) return 0; } static inline void pci_remove_newid_file(struct pci_driver *drv) {} +static inline int pci_create_removeid_file(struct pci_driver *drv) +{ + return 0; +} +static inline void pci_remove_removeid_file(struct pci_driver *drv) {} #endif /** @@ -852,13 +917,23 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, /* register with core */ error = driver_register(&drv->driver); if (error) - return error; + goto out; error = pci_create_newid_file(drv); if (error) - driver_unregister(&drv->driver); + goto out_newid; + error = pci_create_removeid_file(drv); + if (error) + goto out_removeid; +out: return error; + +out_removeid: + pci_remove_newid_file(drv); +out_newid: + driver_unregister(&drv->driver); + goto out; } /** @@ -874,6 +949,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, void pci_unregister_driver(struct pci_driver *drv) { + pci_remove_removeid_file(drv); pci_remove_newid_file(drv); driver_unregister(&drv->driver); pci_free_dynids(drv); -- cgit v1.2.3-59-g8ed1b From c41ade2ee1dc146d2de2ee470a87cd6b878a08f4 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 17 Mar 2009 08:54:05 -0400 Subject: Rewrite MSI-HOWTO I didn't find the previous version very useful, so I rewrote it. Signed-off-by: Matthew Wilcox Reviewed-by: Randy Dunlap Reviewed-by: Grant Grundler Signed-off-by: Jesse Barnes --- Documentation/PCI/MSI-HOWTO.txt | 758 +++++++++++++++------------------------- 1 file changed, 277 insertions(+), 481 deletions(-) (limited to 'Documentation') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index 256defd7e174..1c02431f1d1a 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -4,506 +4,302 @@ Revised Feb 12, 2004 by Martine Silbermann email: Martine.Silbermann@hp.com Revised Jun 25, 2004 by Tom L Nguyen + Revised Jul 9, 2008 by Matthew Wilcox + Copyright 2003, 2008 Intel Corporation 1. About this guide -This guide describes the basics of Message Signaled Interrupts (MSI), -the advantages of using MSI over traditional interrupt mechanisms, -and how to enable your driver to use MSI or MSI-X. Also included is -a Frequently Asked Questions (FAQ) section. - -1.1 Terminology - -PCI devices can be single-function or multi-function. In either case, -when this text talks about enabling or disabling MSI on a "device -function," it is referring to one specific PCI device and function and -not to all functions on a PCI device (unless the PCI device has only -one function). - -2. Copyright 2003 Intel Corporation - -3. What is MSI/MSI-X? - -Message Signaled Interrupt (MSI), as described in the PCI Local Bus -Specification Revision 2.3 or later, is an optional feature, and a -required feature for PCI Express devices. MSI enables a device function -to request service by sending an Inbound Memory Write on its PCI bus to -the FSB as a Message Signal Interrupt transaction. Because MSI is -generated in the form of a Memory Write, all transaction conditions, -such as a Retry, Master-Abort, Target-Abort or normal completion, are -supported. - -A PCI device that supports MSI must also support pin IRQ assertion -interrupt mechanism to provide backward compatibility for systems that -do not support MSI. In systems which support MSI, the bus driver is -responsible for initializing the message address and message data of -the device function's MSI/MSI-X capability structure during device -initial configuration. - -An MSI capable device function indicates MSI support by implementing -the MSI/MSI-X capability structure in its PCI capability list. The -device function may implement both the MSI capability structure and -the MSI-X capability structure; however, the bus driver should not -enable both. - -The MSI capability structure contains Message Control register, -Message Address register and Message Data register. These registers -provide the bus driver control over MSI. The Message Control register -indicates the MSI capability supported by the device. The Message -Address register specifies the target address and the Message Data -register specifies the characteristics of the message. To request -service, the device function writes the content of the Message Data -register to the target address. The device and its software driver -are prohibited from writing to these registers. - -The MSI-X capability structure is an optional extension to MSI. It -uses an independent and separate capability structure. There are -some key advantages to implementing the MSI-X capability structure -over the MSI capability structure as described below. - - - Support a larger maximum number of vectors per function. - - - Provide the ability for system software to configure - each vector with an independent message address and message - data, specified by a table that resides in Memory Space. - - - MSI and MSI-X both support per-vector masking. Per-vector - masking is an optional extension of MSI but a required - feature for MSI-X. Per-vector masking provides the kernel the - ability to mask/unmask a single MSI while running its - interrupt service routine. If per-vector masking is - not supported, then the device driver should provide the - hardware/software synchronization to ensure that the device - generates MSI when the driver wants it to do so. - -4. Why use MSI? - -As a benefit to the simplification of board design, MSI allows board -designers to remove out-of-band interrupt routing. MSI is another -step towards a legacy-free environment. - -Due to increasing pressure on chipset and processor packages to -reduce pin count, the need for interrupt pins is expected to -diminish over time. Devices, due to pin constraints, may implement -messages to increase performance. - -PCI Express endpoints uses INTx emulation (in-band messages) instead -of IRQ pin assertion. Using INTx emulation requires interrupt -sharing among devices connected to the same node (PCI bridge) while -MSI is unique (non-shared) and does not require BIOS configuration -support. As a result, the PCI Express technology requires MSI -support for better interrupt performance. - -Using MSI enables the device functions to support two or more -vectors, which can be configured to target different CPUs to -increase scalability. - -5. Configuring a driver to use MSI/MSI-X - -By default, the kernel will not enable MSI/MSI-X on all devices that -support this capability. The CONFIG_PCI_MSI kernel option -must be selected to enable MSI/MSI-X support. - -5.1 Including MSI/MSI-X support into the kernel - -To allow MSI/MSI-X capable device drivers to selectively enable -MSI/MSI-X (using pci_enable_msi()/pci_enable_msix() as described -below), the VECTOR based scheme needs to be enabled by setting -CONFIG_PCI_MSI during kernel config. - -Since the target of the inbound message is the local APIC, providing -CONFIG_X86_LOCAL_APIC must be enabled as well as CONFIG_PCI_MSI. - -5.2 Configuring for MSI support - -Due to the non-contiguous fashion in vector assignment of the -existing Linux kernel, this version does not support multiple -messages regardless of a device function is capable of supporting -more than one vector. To enable MSI on a device function's MSI -capability structure requires a device driver to call the function -pci_enable_msi() explicitly. - -5.2.1 API pci_enable_msi +This guide describes the basics of Message Signaled Interrupts (MSIs), +the advantages of using MSI over traditional interrupt mechanisms, how +to change your driver to use MSI or MSI-X and some basic diagnostics to +try if a device doesn't support MSIs. + + +2. What are MSIs? + +A Message Signaled Interrupt is a write from the device to a special +address which causes an interrupt to be received by the CPU. + +The MSI capability was first specified in PCI 2.2 and was later enhanced +in PCI 3.0 to allow each interrupt to be masked individually. The MSI-X +capability was also introduced with PCI 3.0. It supports more interrupts +per device than MSI and allows interrupts to be independently configured. + +Devices may support both MSI and MSI-X, but only one can be enabled at +a time. + + +3. Why use MSIs? + +There are three reasons why using MSIs can give an advantage over +traditional pin-based interrupts. + +Pin-based PCI interrupts are often shared amongst several devices. +To support this, the kernel must call each interrupt handler associated +with an interrupt, which leads to reduced performance for the system as +a whole. MSIs are never shared, so this problem cannot arise. + +When a device writes data to memory, then raises a pin-based interrupt, +it is possible that the interrupt may arrive before all the data has +arrived in memory (this becomes more likely with devices behind PCI-PCI +bridges). In order to ensure that all the data has arrived in memory, +the interrupt handler must read a register on the device which raised +the interrupt. PCI transaction ordering rules require that all the data +arrives in memory before the value can be returned from the register. +Using MSIs avoids this problem as the interrupt-generating write cannot +pass the data writes, so by the time the interrupt is raised, the driver +knows that all the data has arrived in memory. + +PCI devices can only support a single pin-based interrupt per function. +Often drivers have to query the device to find out what event has +occurred, slowing down interrupt handling for the common case. With +MSIs, a device can support more interrupts, allowing each interrupt +to be specialised to a different purpose. One possible design gives +infrequent conditions (such as errors) their own interrupt which allows +the driver to handle the normal interrupt handling path more efficiently. +Other possible designs include giving one interrupt to each packet queue +in a network card or each port in a storage controller. + + +4. How to use MSIs + +PCI devices are initialised to use pin-based interrupts. The device +driver has to set up the device to use MSI or MSI-X. Not all machines +support MSIs correctly, and for those machines, the APIs described below +will simply fail and the device will continue to use pin-based interrupts. + +4.1 Include kernel support for MSIs + +To support MSI or MSI-X, the kernel must be built with the CONFIG_PCI_MSI +option enabled. This option is only available on some architectures, +and it may depend on some other options also being set. For example, +on x86, you must also enable X86_UP_APIC or SMP in order to see the +CONFIG_PCI_MSI option. + +4.2 Using MSI + +Most of the hard work is done for the driver in the PCI layer. It simply +has to request that the PCI layer set up the MSI capability for this +device. + +4.2.1 pci_enable_msi int pci_enable_msi(struct pci_dev *dev) -With this new API, a device driver that wants to have MSI -enabled on its device function must call this API to enable MSI. -A successful call will initialize the MSI capability structure -with ONE vector, regardless of whether a device function is -capable of supporting multiple messages. This vector replaces the -pre-assigned dev->irq with a new MSI vector. To avoid a conflict -of the new assigned vector with existing pre-assigned vector requires -a device driver to call this API before calling request_irq(). +A successful call will allocate ONE interrupt to the device, regardless +of how many MSIs the device supports. The device will be switched from +pin-based interrupt mode to MSI mode. The dev->irq number is changed +to a new number which represents the message signaled interrupt. +This function should be called before the driver calls request_irq() +since enabling MSIs disables the pin-based IRQ and the driver will not +receive interrupts on the old interrupt. -5.2.2 API pci_disable_msi +4.2.2 pci_disable_msi void pci_disable_msi(struct pci_dev *dev) -This API should always be used to undo the effect of pci_enable_msi() -when a device driver is unloading. This API restores dev->irq with -the pre-assigned IOAPIC vector and switches a device's interrupt -mode to PCI pin-irq assertion/INTx emulation mode. - -Note that a device driver should always call free_irq() on the MSI vector -that it has done request_irq() on before calling this API. Failure to do -so results in a BUG_ON() and a device will be left with MSI enabled and -leaks its vector. - -5.2.3 MSI mode vs. legacy mode diagram - -The below diagram shows the events which switch the interrupt -mode on the MSI-capable device function between MSI mode and -PIN-IRQ assertion mode. - - ------------ pci_enable_msi ------------------------ - | | <=============== | | - | MSI MODE | | PIN-IRQ ASSERTION MODE | - | | ===============> | | - ------------ pci_disable_msi ------------------------ - - -Figure 1. MSI Mode vs. Legacy Mode - -In Figure 1, a device operates by default in legacy mode. Legacy -in this context means PCI pin-irq assertion or PCI-Express INTx -emulation. A successful MSI request (using pci_enable_msi()) switches -a device's interrupt mode to MSI mode. A pre-assigned IOAPIC vector -stored in dev->irq will be saved by the PCI subsystem and a new -assigned MSI vector will replace dev->irq. - -To return back to its default mode, a device driver should always call -pci_disable_msi() to undo the effect of pci_enable_msi(). Note that a -device driver should always call free_irq() on the MSI vector it has -done request_irq() on before calling pci_disable_msi(). Failure to do -so results in a BUG_ON() and a device will be left with MSI enabled and -leaks its vector. Otherwise, the PCI subsystem restores a device's -dev->irq with a pre-assigned IOAPIC vector and marks the released -MSI vector as unused. - -Once being marked as unused, there is no guarantee that the PCI -subsystem will reserve this MSI vector for a device. Depending on -the availability of current PCI vector resources and the number of -MSI/MSI-X requests from other drivers, this MSI may be re-assigned. - -For the case where the PCI subsystem re-assigns this MSI vector to -another driver, a request to switch back to MSI mode may result -in being assigned a different MSI vector or a failure if no more -vectors are available. - -5.3 Configuring for MSI-X support - -Due to the ability of the system software to configure each vector of -the MSI-X capability structure with an independent message address -and message data, the non-contiguous fashion in vector assignment of -the existing Linux kernel has no impact on supporting multiple -messages on an MSI-X capable device functions. To enable MSI-X on -a device function's MSI-X capability structure requires its device -driver to call the function pci_enable_msix() explicitly. - -The function pci_enable_msix(), once invoked, enables either -all or nothing, depending on the current availability of PCI vector -resources. If the PCI vector resources are available for the number -of vectors requested by a device driver, this function will configure -the MSI-X table of the MSI-X capability structure of a device with -requested messages. To emphasize this reason, for example, a device -may be capable for supporting the maximum of 32 vectors while its -software driver usually may request 4 vectors. It is recommended -that the device driver should call this function once during the -initialization phase of the device driver. - -Unlike the function pci_enable_msi(), the function pci_enable_msix() -does not replace the pre-assigned IOAPIC dev->irq with a new MSI -vector because the PCI subsystem writes the 1:1 vector-to-entry mapping -into the field vector of each element contained in a second argument. -Note that the pre-assigned IOAPIC dev->irq is valid only if the device -operates in PIN-IRQ assertion mode. In MSI-X mode, any attempt at -using dev->irq by the device driver to request for interrupt service -may result in unpredictable behavior. - -For each MSI-X vector granted, a device driver is responsible for calling -other functions like request_irq(), enable_irq(), etc. to enable -this vector with its corresponding interrupt service handler. It is -a device driver's choice to assign all vectors with the same -interrupt service handler or each vector with a unique interrupt -service handler. - -5.3.1 Handling MMIO address space of MSI-X Table - -The PCI 3.0 specification has implementation notes that MMIO address -space for a device's MSI-X structure should be isolated so that the -software system can set different pages for controlling accesses to the -MSI-X structure. The implementation of MSI support requires the PCI -subsystem, not a device driver, to maintain full control of the MSI-X -table/MSI-X PBA (Pending Bit Array) and MMIO address space of the MSI-X -table/MSI-X PBA. A device driver should not access the MMIO address -space of the MSI-X table/MSI-X PBA. - -5.3.2 API pci_enable_msix - -int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) +This function should be used to undo the effect of pci_enable_msi(). +Calling it restores dev->irq to the pin-based interrupt number and frees +the previously allocated message signaled interrupt(s). The interrupt +may subsequently be assigned to another device, so drivers should not +cache the value of dev->irq. -This API enables a device driver to request the PCI subsystem -to enable MSI-X messages on its hardware device. Depending on -the availability of PCI vectors resources, the PCI subsystem enables -either all or none of the requested vectors. +A device driver must always call free_irq() on the interrupt(s) +for which it has called request_irq() before calling this function. +Failure to do so will result in a BUG_ON(), the device will be left with +MSI enabled and will leak its vector. -Argument 'dev' points to the device (pci_dev) structure. +4.3 Using MSI-X -Argument 'entries' is a pointer to an array of msix_entry structs. -The number of entries is indicated in argument 'nvec'. -struct msix_entry is defined in /driver/pci/msi.h: +The MSI-X capability is much more flexible than the MSI capability. +It supports up to 2048 interrupts, each of which can be controlled +independently. To support this flexibility, drivers must use an array of +`struct msix_entry': struct msix_entry { u16 vector; /* kernel uses to write alloc vector */ u16 entry; /* driver uses to specify entry */ }; -A device driver is responsible for initializing the field 'entry' of -each element with a unique entry supported by MSI-X table. Otherwise, --EINVAL will be returned as a result. A successful return of zero -indicates the PCI subsystem completed initializing each of the requested -entries of the MSI-X table with message address and message data. -Last but not least, the PCI subsystem will write the 1:1 -vector-to-entry mapping into the field 'vector' of each element. A -device driver is responsible for keeping track of allocated MSI-X -vectors in its internal data structure. - -A return of zero indicates that the number of MSI-X vectors was -successfully allocated. A return of greater than zero indicates -MSI-X vector shortage. Or a return of less than zero indicates -a failure. This failure may be a result of duplicate entries -specified in second argument, or a result of no available vector, -or a result of failing to initialize MSI-X table entries. - -5.3.3 API pci_disable_msix +This allows for the device to use these interrupts in a sparse fashion; +for example it could use interrupts 3 and 1027 and allocate only a +two-element array. The driver is expected to fill in the 'entry' value +in each element of the array to indicate which entries it wants the kernel +to assign interrupts for. It is invalid to fill in two entries with the +same number. + +4.3.1 pci_enable_msix + +int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) + +Calling this function asks the PCI subsystem to allocate 'nvec' MSIs. +The 'entries' argument is a pointer to an array of msix_entry structs +which should be at least 'nvec' entries in size. On success, the +function will return 0 and the device will have been switched into +MSI-X interrupt mode. The 'vector' elements in each entry will have +been filled in with the interrupt number. The driver should then call +request_irq() for each 'vector' that it decides to use. + +If this function returns a negative number, it indicates an error and +the driver should not attempt to allocate any more MSI-X interrupts for +this device. If it returns a positive number, it indicates the maximum +number of interrupt vectors that could have been allocated. + +This function, in contrast with pci_enable_msi(), does not adjust +dev->irq. The device will not generate interrupts for this interrupt +number once MSI-X is enabled. The device driver is responsible for +keeping track of the interrupts assigned to the MSI-X vectors so it can +free them again later. + +Device drivers should normally call this function once per device +during the initialization phase. + +4.3.2 pci_disable_msix void pci_disable_msix(struct pci_dev *dev) -This API should always be used to undo the effect of pci_enable_msix() -when a device driver is unloading. Note that a device driver should -always call free_irq() on all MSI-X vectors it has done request_irq() -on before calling this API. Failure to do so results in a BUG_ON() and -a device will be left with MSI-X enabled and leaks its vectors. - -5.3.4 MSI-X mode vs. legacy mode diagram - -The below diagram shows the events which switch the interrupt -mode on the MSI-X capable device function between MSI-X mode and -PIN-IRQ assertion mode (legacy). - - ------------ pci_enable_msix(,,n) ------------------------ - | | <=============== | | - | MSI-X MODE | | PIN-IRQ ASSERTION MODE | - | | ===============> | | - ------------ pci_disable_msix ------------------------ - -Figure 2. MSI-X Mode vs. Legacy Mode - -In Figure 2, a device operates by default in legacy mode. A -successful MSI-X request (using pci_enable_msix()) switches a -device's interrupt mode to MSI-X mode. A pre-assigned IOAPIC vector -stored in dev->irq will be saved by the PCI subsystem; however, -unlike MSI mode, the PCI subsystem will not replace dev->irq with -assigned MSI-X vector because the PCI subsystem already writes the 1:1 -vector-to-entry mapping into the field 'vector' of each element -specified in second argument. - -To return back to its default mode, a device driver should always call -pci_disable_msix() to undo the effect of pci_enable_msix(). Note that -a device driver should always call free_irq() on all MSI-X vectors it -has done request_irq() on before calling pci_disable_msix(). Failure -to do so results in a BUG_ON() and a device will be left with MSI-X -enabled and leaks its vectors. Otherwise, the PCI subsystem switches a -device function's interrupt mode from MSI-X mode to legacy mode and -marks all allocated MSI-X vectors as unused. - -Once being marked as unused, there is no guarantee that the PCI -subsystem will reserve these MSI-X vectors for a device. Depending on -the availability of current PCI vector resources and the number of -MSI/MSI-X requests from other drivers, these MSI-X vectors may be -re-assigned. - -For the case where the PCI subsystem re-assigned these MSI-X vectors -to other drivers, a request to switch back to MSI-X mode may result -being assigned with another set of MSI-X vectors or a failure if no -more vectors are available. - -5.4 Handling function implementing both MSI and MSI-X capabilities - -For the case where a function implements both MSI and MSI-X -capabilities, the PCI subsystem enables a device to run either in MSI -mode or MSI-X mode but not both. A device driver determines whether it -wants MSI or MSI-X enabled on its hardware device. Once a device -driver requests for MSI, for example, it is prohibited from requesting -MSI-X; in other words, a device driver is not permitted to ping-pong -between MSI mod MSI-X mode during a run-time. - -5.5 Hardware requirements for MSI/MSI-X support - -MSI/MSI-X support requires support from both system hardware and -individual hardware device functions. - -5.5.1 Required x86 hardware support - -Since the target of MSI address is the local APIC CPU, enabling -MSI/MSI-X support in the Linux kernel is dependent on whether existing -system hardware supports local APIC. Users should verify that their -system supports local APIC operation by testing that it runs when -CONFIG_X86_LOCAL_APIC=y. - -In SMP environment, CONFIG_X86_LOCAL_APIC is automatically set; -however, in UP environment, users must manually set -CONFIG_X86_LOCAL_APIC. Once CONFIG_X86_LOCAL_APIC=y, setting -CONFIG_PCI_MSI enables the VECTOR based scheme and the option for -MSI-capable device drivers to selectively enable MSI/MSI-X. - -Note that CONFIG_X86_IO_APIC setting is irrelevant because MSI/MSI-X -vector is allocated new during runtime and MSI/MSI-X support does not -depend on BIOS support. This key independency enables MSI/MSI-X -support on future IOxAPIC free platforms. - -5.5.2 Device hardware support - -The hardware device function supports MSI by indicating the -MSI/MSI-X capability structure on its PCI capability list. By -default, this capability structure will not be initialized by -the kernel to enable MSI during the system boot. In other words, -the device function is running on its default pin assertion mode. -Note that in many cases the hardware supporting MSI have bugs, -which may result in system hangs. The software driver of specific -MSI-capable hardware is responsible for deciding whether to call -pci_enable_msi or not. A return of zero indicates the kernel -successfully initialized the MSI/MSI-X capability structure of the -device function. The device function is now running on MSI/MSI-X mode. - -5.6 How to tell whether MSI/MSI-X is enabled on device function - -At the driver level, a return of zero from the function call of -pci_enable_msi()/pci_enable_msix() indicates to a device driver that -its device function is initialized successfully and ready to run in -MSI/MSI-X mode. - -At the user level, users can use the command 'cat /proc/interrupts' -to display the vectors allocated for devices and their interrupt -MSI/MSI-X modes ("PCI-MSI"/"PCI-MSI-X"). Below shows MSI mode is -enabled on a SCSI Adaptec 39320D Ultra320 controller. - - CPU0 CPU1 - 0: 324639 0 IO-APIC-edge timer - 1: 1186 0 IO-APIC-edge i8042 - 2: 0 0 XT-PIC cascade - 12: 2797 0 IO-APIC-edge i8042 - 14: 6543 0 IO-APIC-edge ide0 - 15: 1 0 IO-APIC-edge ide1 -169: 0 0 IO-APIC-level uhci-hcd -185: 0 0 IO-APIC-level uhci-hcd -193: 138 10 PCI-MSI aic79xx -201: 30 0 PCI-MSI aic79xx -225: 30 0 IO-APIC-level aic7xxx -233: 30 0 IO-APIC-level aic7xxx -NMI: 0 0 -LOC: 324553 325068 -ERR: 0 -MIS: 0 - -6. MSI quirks - -Several PCI chipsets or devices are known to not support MSI. -The PCI stack provides 3 possible levels of MSI disabling: -* on a single device -* on all devices behind a specific bridge -* globally - -6.1. Disabling MSI on a single device - -Under some circumstances it might be required to disable MSI on a -single device. This may be achieved by either not calling pci_enable_msi() -or all, or setting the pci_dev->no_msi flag before (most of the time -in a quirk). - -6.2. Disabling MSI below a bridge - -The vast majority of MSI quirks are required by PCI bridges not -being able to route MSI between busses. In this case, MSI have to be -disabled on all devices behind this bridge. It is achieves by setting -the PCI_BUS_FLAGS_NO_MSI flag in the pci_bus->bus_flags of the bridge -subordinate bus. There is no need to set the same flag on bridges that -are below the broken bridge. When pci_enable_msi() is called to enable -MSI on a device, pci_msi_supported() takes care of checking the NO_MSI -flag in all parent busses of the device. - -Some bridges actually support dynamic MSI support enabling/disabling -by changing some bits in their PCI configuration space (especially -the Hypertransport chipsets such as the nVidia nForce and Serverworks -HT2000). It may then be required to update the NO_MSI flag on the -corresponding devices in the sysfs hierarchy. To enable MSI support -on device "0000:00:0e", do: - - echo 1 > /sys/bus/pci/devices/0000:00:0e/msi_bus - -To disable MSI support, echo 0 instead of 1. Note that it should be -used with caution since changing this value might break interrupts. - -6.3. Disabling MSI globally - -Some extreme cases may require to disable MSI globally on the system. -For now, the only known case is a Serverworks PCI-X chipsets (MSI are -not supported on several busses that are not all connected to the -chipset in the Linux PCI hierarchy). In the vast majority of other -cases, disabling only behind a specific bridge is enough. - -For debugging purpose, the user may also pass pci=nomsi on the kernel -command-line to explicitly disable MSI globally. But, once the appro- -priate quirks are added to the kernel, this option should not be -required anymore. - -6.4. Finding why MSI cannot be enabled on a device - -Assuming that MSI are not enabled on a device, you should look at -dmesg to find messages that quirks may output when disabling MSI -on some devices, some bridges or even globally. -Then, lspci -t gives the list of bridges above a device. Reading -/sys/bus/pci/devices/0000:00:0e/msi_bus will tell you whether MSI -are enabled (1) or disabled (0). In 0 is found in a single bridge -msi_bus file above the device, MSI cannot be enabled. - -7. FAQ - -Q1. Are there any limitations on using the MSI? - -A1. If the PCI device supports MSI and conforms to the -specification and the platform supports the APIC local bus, -then using MSI should work. - -Q2. Will it work on all the Pentium processors (P3, P4, Xeon, -AMD processors)? In P3 IPI's are transmitted on the APIC local -bus and in P4 and Xeon they are transmitted on the system -bus. Are there any implications with this? - -A2. MSI support enables a PCI device sending an inbound -memory write (0xfeexxxxx as target address) on its PCI bus -directly to the FSB. Since the message address has a -redirection hint bit cleared, it should work. - -Q3. The target address 0xfeexxxxx will be translated by the -Host Bridge into an interrupt message. Are there any -limitations on the chipsets such as Intel 8xx, Intel e7xxx, -or VIA? - -A3. If these chipsets support an inbound memory write with -target address set as 0xfeexxxxx, as conformed to PCI -specification 2.3 or latest, then it should work. - -Q4. From the driver point of view, if the MSI is lost because -of errors occurring during inbound memory write, then it may -wait forever. Is there a mechanism for it to recover? - -A4. Since the target of the transaction is an inbound memory -write, all transaction termination conditions (Retry, -Master-Abort, Target-Abort, or normal completion) are -supported. A device sending an MSI must abide by all the PCI -rules and conditions regarding that inbound memory write. So, -if a retry is signaled it must retry, etc... We believe that -the recommendation for Abort is also a retry (refer to PCI -specification 2.3 or latest). +This API should be used to undo the effect of pci_enable_msix(). It frees +the previously allocated message signaled interrupts. The interrupts may +subsequently be assigned to another device, so drivers should not cache +the value of the 'vector' elements over a call to pci_disable_msix(). + +A device driver must always call free_irq() on the interrupt(s) +for which it has called request_irq() before calling this function. +Failure to do so will result in a BUG_ON(), the device will be left with +MSI enabled and will leak its vector. + +4.3.3 The MSI-X Table + +The MSI-X capability specifies a BAR and offset within that BAR for the +MSI-X Table. This address is mapped by the PCI subsystem, and should not +be accessed directly by the device driver. If the driver wishes to +mask or unmask an interrupt, it should call disable_irq() / enable_irq(). + +4.4 Handling devices implementing both MSI and MSI-X capabilities + +If a device implements both MSI and MSI-X capabilities, it can +run in either MSI mode or MSI-X mode but not both simultaneously. +This is a requirement of the PCI spec, and it is enforced by the +PCI layer. Calling pci_enable_msi() when MSI-X is already enabled or +pci_enable_msix() when MSI is already enabled will result in an error. +If a device driver wishes to switch between MSI and MSI-X at runtime, +it must first quiesce the device, then switch it back to pin-interrupt +mode, before calling pci_enable_msi() or pci_enable_msix() and resuming +operation. This is not expected to be a common operation but may be +useful for debugging or testing during development. + +4.5 Considerations when using MSIs + +4.5.1 Choosing between MSI-X and MSI + +If your device supports both MSI-X and MSI capabilities, you should use +the MSI-X facilities in preference to the MSI facilities. As mentioned +above, MSI-X supports any number of interrupts between 1 and 2048. +In constrast, MSI is restricted to a maximum of 32 interrupts (and +must be a power of two). In addition, the MSI interrupt vectors must +be allocated consecutively, so the system may not be able to allocate +as many vectors for MSI as it could for MSI-X. On some platforms, MSI +interrupts must all be targetted at the same set of CPUs whereas MSI-X +interrupts can all be targetted at different CPUs. + +4.5.2 Spinlocks + +Most device drivers have a per-device spinlock which is taken in the +interrupt handler. With pin-based interrupts or a single MSI, it is not +necessary to disable interrupts (Linux guarantees the same interrupt will +not be re-entered). If a device uses multiple interrupts, the driver +must disable interrupts while the lock is held. If the device sends +a different interrupt, the driver will deadlock trying to recursively +acquire the spinlock. + +There are two solutions. The first is to take the lock with +spin_lock_irqsave() or spin_lock_irq() (see +Documentation/DocBook/kernel-locking). The second is to specify +IRQF_DISABLED to request_irq() so that the kernel runs the entire +interrupt routine with interrupts disabled. + +If your MSI interrupt routine does not hold the lock for the whole time +it is running, the first solution may be best. The second solution is +normally preferred as it avoids making two transitions from interrupt +disabled to enabled and back again. + +4.6 How to tell whether MSI/MSI-X is enabled on a device + +Using 'lspci -v' (as root) may show some devices with "MSI", "Message +Signalled Interrupts" or "MSI-X" capabilities. Each of these capabilities +has an 'Enable' flag which will be followed with either "+" (enabled) +or "-" (disabled). + + +5. MSI quirks + +Several PCI chipsets or devices are known not to support MSIs. +The PCI stack provides three ways to disable MSIs: + +1. globally +2. on all devices behind a specific bridge +3. on a single device + +5.1. Disabling MSIs globally + +Some host chipsets simply don't support MSIs properly. If we're +lucky, the manufacturer knows this and has indicated it in the ACPI +FADT table. In this case, Linux will automatically disable MSIs. +Some boards don't include this information in the table and so we have +to detect them ourselves. The complete list of these is found near the +quirk_disable_all_msi() function in drivers/pci/quirks.c. + +If you have a board which has problems with MSIs, you can pass pci=nomsi +on the kernel command line to disable MSIs on all devices. It would be +in your best interests to report the problem to linux-pci@vger.kernel.org +including a full 'lspci -v' so we can add the quirks to the kernel. + +5.2. Disabling MSIs below a bridge + +Some PCI bridges are not able to route MSIs between busses properly. +In this case, MSIs must be disabled on all devices behind the bridge. + +Some bridges allow you to enable MSIs by changing some bits in their +PCI configuration space (especially the Hypertransport chipsets such +as the nVidia nForce and Serverworks HT2000). As with host chipsets, +Linux mostly knows about them and automatically enables MSIs if it can. +If you have a bridge which Linux doesn't yet know about, you can enable +MSIs in configuration space using whatever method you know works, then +enable MSIs on that bridge by doing: + + echo 1 > /sys/bus/pci/devices/$bridge/msi_bus + +where $bridge is the PCI address of the bridge you've enabled (eg +0000:00:0e.0). + +To disable MSIs, echo 0 instead of 1. Changing this value should be +done with caution as it can break interrupt handling for all devices +below this bridge. + +Again, please notify linux-pci@vger.kernel.org of any bridges that need +special handling. + +5.3. Disabling MSIs on a single device + +Some devices are known to have faulty MSI implementations. Usually this +is handled in the individual device driver but occasionally it's necessary +to handle this with a quirk. Some drivers have an option to disable use +of MSI. While this is a convenient workaround for the driver author, +it is not good practise, and should not be emulated. + +5.4. Finding why MSIs are disabled on a device + +From the above three sections, you can see that there are many reasons +why MSIs may not be enabled for a given device. Your first step should +be to examine your dmesg carefully to determine whether MSIs are enabled +for your machine. You should also check your .config to be sure you +have enabled CONFIG_PCI_MSI. + +Then, 'lspci -t' gives the list of bridges above a device. Reading +/sys/bus/pci/devices/*/msi_bus will tell you whether MSI are enabled (1) +or disabled (0). If 0 is found in any of the msi_bus files belonging +to bridges between the PCI root and the device, MSIs are disabled. + +It is also worth checking the device driver to see whether it supports MSIs. +For example, it may contain calls to pci_enable_msi(), pci_enable_msix() or +pci_enable_msi_block(). -- cgit v1.2.3-59-g8ed1b From 1c8d7b0a562da06d3ebe83f01b1ed553205d1ae4 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 17 Mar 2009 08:54:10 -0400 Subject: PCI MSI: Add support for multiple MSI Add the new API pci_enable_msi_block() to allow drivers to request multiple MSI and reimplement pci_enable_msi in terms of pci_enable_msi_block. Ensure that the architecture back ends don't have to know about multiple MSI. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- Documentation/PCI/MSI-HOWTO.txt | 45 +++++++++++++++++--- arch/powerpc/kernel/msi.c | 4 ++ arch/x86/kernel/io_apic.c | 4 ++ drivers/pci/msi.c | 91 +++++++++++++++++++++++++++++------------ drivers/pci/msi.h | 6 --- include/linux/msi.h | 1 + include/linux/pci.h | 6 ++- 7 files changed, 116 insertions(+), 41 deletions(-) (limited to 'Documentation') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index 1c02431f1d1a..9494f6dc38eb 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -94,15 +94,48 @@ This function should be called before the driver calls request_irq() since enabling MSIs disables the pin-based IRQ and the driver will not receive interrupts on the old interrupt. -4.2.2 pci_disable_msi +4.2.2 pci_enable_msi_block + +int pci_enable_msi_block(struct pci_dev *dev, int count) + +This variation on the above call allows a device driver to request multiple +MSIs. The MSI specification only allows interrupts to be allocated in +powers of two, up to a maximum of 2^5 (32). + +If this function returns 0, it has succeeded in allocating at least as many +interrupts as the driver requested (it may have allocated more in order +to satisfy the power-of-two requirement). In this case, the function +enables MSI on this device and updates dev->irq to be the lowest of +the new interrupts assigned to it. The other interrupts assigned to +the device are in the range dev->irq to dev->irq + count - 1. + +If this function returns a negative number, it indicates an error and +the driver should not attempt to request any more MSI interrupts for +this device. If this function returns a positive number, it will be +less than 'count' and indicate the number of interrupts that could have +been allocated. In neither case will the irq value have been +updated, nor will the device have been switched into MSI mode. + +The device driver must decide what action to take if +pci_enable_msi_block() returns a value less than the number asked for. +Some devices can make use of fewer interrupts than the maximum they +request; in this case the driver should call pci_enable_msi_block() +again. Note that it is not guaranteed to succeed, even when the +'count' has been reduced to the value returned from a previous call to +pci_enable_msi_block(). This is because there are multiple constraints +on the number of vectors that can be allocated; pci_enable_msi_block() +will return as soon as it finds any constraint that doesn't allow the +call to succeed. + +4.2.3 pci_disable_msi void pci_disable_msi(struct pci_dev *dev) -This function should be used to undo the effect of pci_enable_msi(). -Calling it restores dev->irq to the pin-based interrupt number and frees -the previously allocated message signaled interrupt(s). The interrupt -may subsequently be assigned to another device, so drivers should not -cache the value of dev->irq. +This function should be used to undo the effect of pci_enable_msi() or +pci_enable_msi_block(). Calling it restores dev->irq to the pin-based +interrupt number and frees the previously allocated message signaled +interrupt(s). The interrupt may subsequently be assigned to another +device, so drivers should not cache the value of dev->irq. A device driver must always call free_irq() on the interrupt(s) for which it has called request_irq() before calling this function. diff --git a/arch/powerpc/kernel/msi.c b/arch/powerpc/kernel/msi.c index 3bb7d3dd28be..0c16e2a854e5 100644 --- a/arch/powerpc/kernel/msi.c +++ b/arch/powerpc/kernel/msi.c @@ -19,6 +19,10 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type) return -ENOSYS; } + /* PowerPC doesn't support multiple MSI yet */ + if (type == PCI_CAP_ID_MSI && nvec > 1) + return 1; + if (ppc_md.msi_check_device) { pr_debug("msi: Using platform check routine.\n"); return ppc_md.msi_check_device(dev, nvec, type); diff --git a/arch/x86/kernel/io_apic.c b/arch/x86/kernel/io_apic.c index bc7ac4da90d7..a09549a6321b 100644 --- a/arch/x86/kernel/io_apic.c +++ b/arch/x86/kernel/io_apic.c @@ -3510,6 +3510,10 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) int index = 0; #endif + /* x86 doesn't support multiple MSI yet */ + if (type == PCI_CAP_ID_MSI && nvec > 1) + return 1; + irq_want = nr_irqs_gsi; sub_handle = 0; list_for_each_entry(msidesc, &dev->msi_list, list) { diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index adcc78242571..6f2e6295e773 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -40,6 +40,13 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) struct msi_desc *entry; int ret; + /* + * If an architecture wants to support multiple MSI, it needs to + * override arch_setup_msi_irqs() + */ + if (type == PCI_CAP_ID_MSI && nvec > 1) + return 1; + list_for_each_entry(entry, &dev->msi_list, list) { ret = arch_setup_msi_irq(dev, entry); if (ret < 0) @@ -58,8 +65,12 @@ void arch_teardown_msi_irqs(struct pci_dev *dev) struct msi_desc *entry; list_for_each_entry(entry, &dev->msi_list, list) { - if (entry->irq != 0) - arch_teardown_msi_irq(entry->irq); + int i, nvec; + if (entry->irq == 0) + continue; + nvec = 1 << entry->msi_attrib.multiple; + for (i = 0; i < nvec; i++) + arch_teardown_msi_irq(entry->irq + i); } } #endif @@ -163,7 +174,8 @@ static void msi_set_mask_bit(unsigned irq, u32 flag) msix_mask_irq(desc, flag); readl(desc->mask_base); /* Flush write to device */ } else { - msi_mask_irq(desc, 1, flag); + unsigned offset = irq - desc->dev->irq; + msi_mask_irq(desc, 1 << offset, flag << offset); } } @@ -229,6 +241,12 @@ void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) } else { struct pci_dev *dev = entry->dev; int pos = entry->msi_attrib.pos; + u16 msgctl; + + pci_read_config_word(dev, msi_control_reg(pos), &msgctl); + msgctl &= ~PCI_MSI_FLAGS_QSIZE; + msgctl |= entry->msi_attrib.multiple << 4; + pci_write_config_word(dev, msi_control_reg(pos), msgctl); pci_write_config_dword(dev, msi_lower_address_reg(pos), msg->address_lo); @@ -291,7 +309,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); msi_mask_irq(entry, msi_capable_mask(control), entry->masked); control &= ~PCI_MSI_FLAGS_QSIZE; - control |= PCI_MSI_FLAGS_ENABLE; + control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); } @@ -332,13 +350,15 @@ EXPORT_SYMBOL_GPL(pci_restore_msi_state); /** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function + * @nvec: number of interrupts to allocate * - * Setup the MSI capability structure of device function with a single - * MSI irq, regardless of device function is capable of handling - * multiple messages. A return of zero indicates the successful setup - * of an entry zero with the new MSI irq or non-zero for otherwise. - **/ -static int msi_capability_init(struct pci_dev *dev) + * Setup the MSI capability structure of the device with the requested + * number of interrupts. A return value of zero indicates the successful + * setup of an entry with the new MSI irq. A negative return value indicates + * an error, and a positive return value indicates the number of interrupts + * which could have been allocated. + */ +static int msi_capability_init(struct pci_dev *dev, int nvec) { struct msi_desc *entry; int pos, ret; @@ -371,7 +391,7 @@ static int msi_capability_init(struct pci_dev *dev) list_add_tail(&entry->list, &dev->msi_list); /* Configure MSI capability structure */ - ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI); + ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); if (ret) { msi_free_irqs(dev); return ret; @@ -524,35 +544,48 @@ static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type) } /** - * pci_enable_msi - configure device's MSI capability structure - * @dev: pointer to the pci_dev data structure of MSI device function + * pci_enable_msi_block - configure device's MSI capability structure + * @dev: device to configure + * @nvec: number of interrupts to configure * - * Setup the MSI capability structure of device function with - * a single MSI irq upon its software driver call to request for - * MSI mode enabled on its hardware device function. A return of zero - * indicates the successful setup of an entry zero with the new MSI - * irq or non-zero for otherwise. - **/ -int pci_enable_msi(struct pci_dev* dev) + * Allocate IRQs for a device with the MSI capability. + * This function returns a negative errno if an error occurs. If it + * is unable to allocate the number of interrupts requested, it returns + * the number of interrupts it might be able to allocate. If it successfully + * allocates at least the number of interrupts requested, it returns 0 and + * updates the @dev's irq member to the lowest new interrupt number; the + * other interrupt numbers allocated to this device are consecutive. + */ +int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) { - int status; + int status, pos, maxvec; + u16 msgctl; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (!pos) + return -EINVAL; + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); + maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); + if (nvec > maxvec) + return maxvec; - status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI); + status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); if (status) return status; WARN_ON(!!dev->msi_enabled); - /* Check whether driver already requested for MSI-X irqs */ + /* Check whether driver already requested MSI-X irqs */ if (dev->msix_enabled) { dev_info(&dev->dev, "can't enable MSI " "(MSI-X already enabled)\n"); return -EINVAL; } - status = msi_capability_init(dev); + + status = msi_capability_init(dev, nvec); return status; } -EXPORT_SYMBOL(pci_enable_msi); +EXPORT_SYMBOL(pci_enable_msi_block); void pci_msi_shutdown(struct pci_dev *dev) { @@ -599,8 +632,12 @@ static int msi_free_irqs(struct pci_dev* dev) struct msi_desc *entry, *tmp; list_for_each_entry(entry, &dev->msi_list, list) { - if (entry->irq) - BUG_ON(irq_has_action(entry->irq)); + int i, nvec; + if (!entry->irq) + continue; + nvec = 1 << entry->msi_attrib.multiple; + for (i = 0; i < nvec; i++) + BUG_ON(irq_has_action(entry->irq + i)); } arch_teardown_msi_irqs(dev); diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index 3898f5237144..71f4df2ef654 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h @@ -20,14 +20,8 @@ #define msi_mask_bits_reg(base, is64bit) \ ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) #define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE -#define multi_msi_capable(control) \ - (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) -#define multi_msi_enable(control, num) \ - control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); #define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) #define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) -#define msi_enable(control, num) multi_msi_enable(control, num); \ - control |= PCI_MSI_FLAGS_ENABLE #define msix_table_offset_reg(base) (base + 0x04) #define msix_pba_offset_reg(base) (base + 0x08) diff --git a/include/linux/msi.h b/include/linux/msi.h index 37c1bbe546e5..6991ab5b24d1 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -21,6 +21,7 @@ extern void write_msi_msg(unsigned int irq, struct msi_msg *msg); struct msi_desc { struct { __u8 is_msix : 1; + __u8 multiple: 3; /* log2 number of messages */ __u8 maskbit : 1; /* mask-pending bit supported ? */ __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ __u8 pos; /* Location of the msi capability */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 7baf2a5db12a..1f6c5ddaae36 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -789,7 +789,7 @@ struct msix_entry { #ifndef CONFIG_PCI_MSI -static inline int pci_enable_msi(struct pci_dev *dev) +static inline int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) { return -1; } @@ -824,7 +824,7 @@ static inline int pci_msi_enabled(void) return 0; } #else -extern int pci_enable_msi(struct pci_dev *dev); +extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); extern void pci_msi_shutdown(struct pci_dev *dev); extern void pci_disable_msi(struct pci_dev *dev); extern int pci_msix_table_size(struct pci_dev *dev); @@ -846,6 +846,8 @@ static inline int pcie_aspm_enabled(void) extern int pcie_aspm_enabled(void); #endif +#define pci_enable_msi(pdev) pci_enable_msi_block(pdev, 1) + #ifdef CONFIG_HT_IRQ /* The functions a driver should call */ int ht_create_irq(struct pci_dev *dev, int idx); -- cgit v1.2.3-59-g8ed1b From 32a9a682bef2f6fce7026bd94d1ce20028b0e52d Mon Sep 17 00:00:00 2001 From: Yuji Shimada Date: Mon, 16 Mar 2009 17:13:39 +0900 Subject: PCI: allow assignment of memory resources with a specified alignment This patch allows memory resources to be assigned with a specified alignment at boot-time or run-time. The patch is useful when we use PCI pass-through, because page-aligned memory resources are required to securely share PCI resources with guest drivers. If you want to assign the resource at boot time, please set "pci=resource_alignment=" boot parameter. This is format of "pci=resource_alignment=" boot parameter: [@][:]:.[; ...] Specifies alignment and device to reassign aligned memory resources. If is not specified, PAGE_SIZE is used as alignment. PCI-PCI bridge can be specified, if resource windows need to be expanded. This is example: pci=resource_alignment=20@07:00.0;18@0f:00.0;00:1d.7 If you want to assign the resource at run-time, please set "/sys/bus/pci/resource_alignment" file, and hot-remove the device and hot-add the device. For this purpose, fakephp or PCI hotplug interfaces can be used. The format of "/sys/bus/pci/resource_alignment" file is the same with boot parameter. You can use "," instead of ";". For example: # cd /sys/bus/pci # echo -n 20@12:00.0 > resource_alignment # echo 1 > devices/0000:12:00.0/remove # echo 1 > rescan Reviewed-by: Alex Chiang Reviewed-by: Yu Zhao Signed-off-by: Yuji Shimada Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 9 +++ drivers/pci/pci.c | 120 ++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 6 ++ drivers/pci/quirks.c | 60 ++++++++++++++++++ drivers/pci/setup-res.c | 15 +++++ 5 files changed, 210 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c7c441e7930e..1754fedc531c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1760,6 +1760,15 @@ and is between 256 and 4096 characters. It is defined in the file cbmemsize=nn[KMG] The fixed amount of bus space which is reserved for the CardBus bridge's memory window. The default value is 64 megabytes. + resource_alignment= + Format: + [@][:]:.[; ...] + Specifies alignment and device to reassign + aligned memory resources. + If is not specified, + PAGE_SIZE is used as alignment. + PCI-PCI bridge can be specified, if resource + windows need to be expanded. pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power Management. diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8310dc2f943b..a35a8b2ba631 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -20,6 +20,8 @@ #include #include #include /* isa_dma_bridge_buggy */ +#include +#include #include "pci.h" unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; @@ -2370,6 +2372,121 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) return 0; } +#define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE +static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0}; +spinlock_t resource_alignment_lock = SPIN_LOCK_UNLOCKED; + +/** + * pci_specified_resource_alignment - get resource alignment specified by user. + * @dev: the PCI device to get + * + * RETURNS: Resource alignment if it is specified. + * Zero if it is not specified. + */ +resource_size_t pci_specified_resource_alignment(struct pci_dev *dev) +{ + int seg, bus, slot, func, align_order, count; + resource_size_t align = 0; + char *p; + + spin_lock(&resource_alignment_lock); + p = resource_alignment_param; + while (*p) { + count = 0; + if (sscanf(p, "%d%n", &align_order, &count) == 1 && + p[count] == '@') { + p += count + 1; + } else { + align_order = -1; + } + if (sscanf(p, "%x:%x:%x.%x%n", + &seg, &bus, &slot, &func, &count) != 4) { + seg = 0; + if (sscanf(p, "%x:%x.%x%n", + &bus, &slot, &func, &count) != 3) { + /* Invalid format */ + printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n", + p); + break; + } + } + p += count; + if (seg == pci_domain_nr(dev->bus) && + bus == dev->bus->number && + slot == PCI_SLOT(dev->devfn) && + func == PCI_FUNC(dev->devfn)) { + if (align_order == -1) { + align = PAGE_SIZE; + } else { + align = 1 << align_order; + } + /* Found */ + break; + } + if (*p != ';' && *p != ',') { + /* End of param or invalid format */ + break; + } + p++; + } + spin_unlock(&resource_alignment_lock); + return align; +} + +/** + * pci_is_reassigndev - check if specified PCI is target device to reassign + * @dev: the PCI device to check + * + * RETURNS: non-zero for PCI device is a target device to reassign, + * or zero is not. + */ +int pci_is_reassigndev(struct pci_dev *dev) +{ + return (pci_specified_resource_alignment(dev) != 0); +} + +ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) +{ + if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) + count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1; + spin_lock(&resource_alignment_lock); + strncpy(resource_alignment_param, buf, count); + resource_alignment_param[count] = '\0'; + spin_unlock(&resource_alignment_lock); + return count; +} + +ssize_t pci_get_resource_alignment_param(char *buf, size_t size) +{ + size_t count; + spin_lock(&resource_alignment_lock); + count = snprintf(buf, size, "%s", resource_alignment_param); + spin_unlock(&resource_alignment_lock); + return count; +} + +static ssize_t pci_resource_alignment_show(struct bus_type *bus, char *buf) +{ + return pci_get_resource_alignment_param(buf, PAGE_SIZE); +} + +static ssize_t pci_resource_alignment_store(struct bus_type *bus, + const char *buf, size_t count) +{ + return pci_set_resource_alignment_param(buf, count); +} + +BUS_ATTR(resource_alignment, 0644, pci_resource_alignment_show, + pci_resource_alignment_store); + +static int __init pci_resource_alignment_sysfs_init(void) +{ + return bus_create_file(&pci_bus_type, + &bus_attr_resource_alignment); +} + +late_initcall(pci_resource_alignment_sysfs_init); + static void __devinit pci_no_domains(void) { #ifdef CONFIG_PCI_DOMAINS @@ -2418,6 +2535,9 @@ static int __init pci_setup(char *str) pci_cardbus_io_size = memparse(str + 9, &str); } else if (!strncmp(str, "cbmemsize=", 10)) { pci_cardbus_mem_size = memparse(str + 10, &str); + } else if (!strncmp(str, "resource_alignment=", 19)) { + pci_set_resource_alignment_param(str + 19, + strlen(str + 19)); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 07c0aa5275e6..2cd1cba7236f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -195,4 +195,10 @@ static inline int pci_ari_enabled(struct pci_bus *bus) return bus->self && bus->self->ari_enabled; } +#ifdef CONFIG_PCI_QUIRKS +extern int pci_is_reassigndev(struct pci_dev *dev); +resource_size_t pci_specified_resource_alignment(struct pci_dev *dev); +extern void pci_disable_bridge_window(struct pci_dev *dev); +#endif + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5aa2afb23ef9..50233818a763 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "pci.h" int isa_dma_bridge_buggy; @@ -34,6 +35,65 @@ int pcie_mch_quirk; EXPORT_SYMBOL(pcie_mch_quirk); #ifdef CONFIG_PCI_QUIRKS +/* + * This quirk function disables the device and releases resources + * which is specified by kernel's boot parameter 'pci=resource_alignment='. + * It also rounds up size to specified alignment. + * Later on, the kernel will assign page-aligned memory resource back + * to that device. + */ +static void __devinit quirk_resource_alignment(struct pci_dev *dev) +{ + int i; + struct resource *r; + resource_size_t align, size; + + if (!pci_is_reassigndev(dev)) + return; + + if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && + (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { + dev_warn(&dev->dev, + "Can't reassign resources to host bridge.\n"); + return; + } + + dev_info(&dev->dev, "Disabling device and release resources.\n"); + pci_disable_device(dev); + + align = pci_specified_resource_alignment(dev); + for (i=0; i < PCI_BRIDGE_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + size = resource_size(r); + if (size < align) { + size = align; + dev_info(&dev->dev, + "Rounding up size of resource #%d to %#llx.\n", + i, (unsigned long long)size); + } + r->end = size - 1; + r->start = 0; + } + /* Need to disable bridge's resource window, + * to enable the kernel to reassign new resource + * window later on. + */ + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + r->end = resource_size(r) - 1; + r->start = 0; + } + pci_disable_bridge_window(dev); + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment); + /* The Mellanox Tavor device gives false positive parity errors * Mark this device with a broken_parity_status, to allow * PCI scanning code to "skip" this now blacklisted device. diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 32e8d88a4619..3039fcb86afc 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -120,6 +120,21 @@ int pci_claim_resource(struct pci_dev *dev, int resource) return err; } +#ifdef CONFIG_PCI_QUIRKS +void pci_disable_bridge_window(struct pci_dev *dev) +{ + dev_dbg(&dev->dev, "Disabling bridge window.\n"); + + /* MMIO Base/Limit */ + pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0); + + /* Prefetchable MMIO Base/Limit */ + pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0); + pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); + pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); +} +#endif /* CONFIG_PCI_QUIRKS */ + int pci_assign_resource(struct pci_dev *dev, int resno) { struct pci_bus *bus = dev->bus; -- cgit v1.2.3-59-g8ed1b From 01db4957179c92fda7d9a06e49b7ae56fb7c925b Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 20 Mar 2009 11:25:17 +0800 Subject: PCI: document SR-IOV sysfs entries Reviewed-by: Randy Dunlap Reviewed-by: Matthew Wilcox Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 3d29793a0ea2..d175a2a5fac2 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -68,3 +68,30 @@ Description: that some devices may have malformatted data. If the underlying VPD has a writable section then the corresponding section of this file will be writable. + +What: /sys/bus/pci/devices/.../virtfnN +Date: March 2009 +Contact: Yu Zhao +Description: + This symbolic link appears when hardware supports the SR-IOV + capability and the Physical Function driver has enabled it. + The symbolic link points to the PCI device sysfs entry of the + Virtual Function whose index is N (0...MaxVFs-1). + +What: /sys/bus/pci/devices/.../dep_link +Date: March 2009 +Contact: Yu Zhao +Description: + This symbolic link appears when hardware supports the SR-IOV + capability and the Physical Function driver has enabled it, + and this device has vendor specific dependencies with others. + The symbolic link points to the PCI device sysfs entry of + Physical Function this device depends on. + +What: /sys/bus/pci/devices/.../physfn +Date: March 2009 +Contact: Yu Zhao +Description: + This symbolic link appears when a device is a Virtual Function. + The symbolic link points to the PCI device sysfs entry of the + Physical Function this device associates with. -- cgit v1.2.3-59-g8ed1b From 15b49bee3a2b228370194f1b3ebc3db427cc9c94 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 20 Mar 2009 11:25:18 +0800 Subject: PCI: manual for SR-IOV user and driver developer Reviewed-by: Randy Dunlap Reviewed-by: Matthew Wilcox Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- Documentation/DocBook/kernel-api.tmpl | 1 + Documentation/PCI/pci-iov-howto.txt | 99 +++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 Documentation/PCI/pci-iov-howto.txt (limited to 'Documentation') diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index bc962cda6504..58c194572c76 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -199,6 +199,7 @@ X!Edrivers/pci/hotplug.c --> !Edrivers/pci/probe.c !Edrivers/pci/rom.c +!Edrivers/pci/iov.c PCI Hotplug Support Library !Edrivers/pci/hotplug/pci_hotplug_core.c diff --git a/Documentation/PCI/pci-iov-howto.txt b/Documentation/PCI/pci-iov-howto.txt new file mode 100644 index 000000000000..fc73ef5d65b8 --- /dev/null +++ b/Documentation/PCI/pci-iov-howto.txt @@ -0,0 +1,99 @@ + PCI Express I/O Virtualization Howto + Copyright (C) 2009 Intel Corporation + Yu Zhao + + +1. Overview + +1.1 What is SR-IOV + +Single Root I/O Virtualization (SR-IOV) is a PCI Express Extended +capability which makes one physical device appear as multiple virtual +devices. The physical device is referred to as Physical Function (PF) +while the virtual devices are referred to as Virtual Functions (VF). +Allocation of the VF can be dynamically controlled by the PF via +registers encapsulated in the capability. By default, this feature is +not enabled and the PF behaves as traditional PCIe device. Once it's +turned on, each VF's PCI configuration space can be accessed by its own +Bus, Device and Function Number (Routing ID). And each VF also has PCI +Memory Space, which is used to map its register set. VF device driver +operates on the register set so it can be functional and appear as a +real existing PCI device. + +2. User Guide + +2.1 How can I enable SR-IOV capability + +The device driver (PF driver) will control the enabling and disabling +of the capability via API provided by SR-IOV core. If the hardware +has SR-IOV capability, loading its PF driver would enable it and all +VFs associated with the PF. + +2.2 How can I use the Virtual Functions + +The VF is treated as hot-plugged PCI devices in the kernel, so they +should be able to work in the same way as real PCI devices. The VF +requires device driver that is same as a normal PCI device's. + +3. Developer Guide + +3.1 SR-IOV API + +To enable SR-IOV capability: + int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); + 'nr_virtfn' is number of VFs to be enabled. + +To disable SR-IOV capability: + void pci_disable_sriov(struct pci_dev *dev); + +To notify SR-IOV core of Virtual Function Migration: + irqreturn_t pci_sriov_migration(struct pci_dev *dev); + +3.2 Usage example + +Following piece of code illustrates the usage of the SR-IOV API. + +static int __devinit dev_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + pci_enable_sriov(dev, NR_VIRTFN); + + ... + + return 0; +} + +static void __devexit dev_remove(struct pci_dev *dev) +{ + pci_disable_sriov(dev); + + ... +} + +static int dev_suspend(struct pci_dev *dev, pm_message_t state) +{ + ... + + return 0; +} + +static int dev_resume(struct pci_dev *dev) +{ + ... + + return 0; +} + +static void dev_shutdown(struct pci_dev *dev) +{ + ... +} + +static struct pci_driver dev_driver = { + .name = "SR-IOV Physical Function driver", + .id_table = dev_id_table, + .probe = dev_probe, + .remove = __devexit_p(dev_remove), + .suspend = dev_suspend, + .resume = dev_resume, + .shutdown = dev_shutdown, +}; -- cgit v1.2.3-59-g8ed1b From fafad5bf06c3a3bb8b24b28b6f065367e7411872 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 20 Mar 2009 15:22:12 +1100 Subject: PCI MSI: Add example request loop to MSI-HOWTO.txt Encourage driver writers to think about supporting a variable number of MSI-X interrupts, and give an example of how to do such a request. Acked-by: Matthew Wilcox Signed-off-by: Michael Ellerman Signed-off-by: Jesse Barnes --- Documentation/PCI/MSI-HOWTO.txt | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index 9494f6dc38eb..dcf7acc720e1 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -176,7 +176,8 @@ request_irq() for each 'vector' that it decides to use. If this function returns a negative number, it indicates an error and the driver should not attempt to allocate any more MSI-X interrupts for this device. If it returns a positive number, it indicates the maximum -number of interrupt vectors that could have been allocated. +number of interrupt vectors that could have been allocated. See example +below. This function, in contrast with pci_enable_msi(), does not adjust dev->irq. The device will not generate interrupts for this interrupt @@ -187,6 +188,26 @@ free them again later. Device drivers should normally call this function once per device during the initialization phase. +It is ideal if drivers can cope with a variable number of MSI-X interrupts, +there are many reasons why the platform may not be able to provide the +exact number a driver asks for. + +A request loop to achieve that might look like: + +static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec) +{ + while (nvec >= FOO_DRIVER_MINIMUM_NVEC) { + rc = pci_enable_msix(adapter->pdev, + adapter->msix_entries, nvec); + if (rc > 0) + nvec = rc; + else + return rc; + } + + return -ENOSPC; +} + 4.3.2 pci_disable_msix void pci_disable_msix(struct pci_dev *dev) -- cgit v1.2.3-59-g8ed1b From 705b1aaa823e800490f157cd9366ad8cff385f5f Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:31 -0600 Subject: PCI: Introduce /sys/bus/pci/rescan This interface allows the user to force a rescan of all PCI buses in system, and rediscover devices that have been removed earlier. pci_bus_attrs implementation from Trent Piepho. Thanks to Vegard Nossum for discovering locking issues with the sysfs interface. Cc: Trent Piepho Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 9 +++++++++ drivers/pci/pci-driver.c | 1 + drivers/pci/pci-sysfs.c | 26 ++++++++++++++++++++++++++ drivers/pci/pci.h | 6 ++++++ drivers/pci/probe.c | 4 ++-- 5 files changed, 44 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index d175a2a5fac2..e6ad047cb3c2 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -57,6 +57,15 @@ Description: match the driver to the device. For example: # echo "8086 10f5" > /sys/bus/pci/drivers/foo/remove_id +What: /sys/bus/pci/rescan +Date: January 2009 +Contact: Linux PCI developers +Description: + Writing a non-zero value to this attribute will + force a rescan of all PCI buses in the system, and + re-discover previously removed devices. + Depends on CONFIG_HOTPLUG. + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 87a5ddbb324f..95d198570290 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1049,6 +1049,7 @@ struct bus_type pci_bus_type = { .remove = pci_device_remove, .shutdown = pci_device_shutdown, .dev_attrs = pci_dev_attrs, + .bus_attrs = pci_bus_attrs, .pm = PCI_PM_OPS_PTR, }; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index ec7a175dcbaf..be7468a5eb72 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -219,6 +219,32 @@ msi_bus_store(struct device *dev, struct device_attribute *attr, return count; } +#ifdef CONFIG_HOTPLUG +static DEFINE_MUTEX(pci_remove_rescan_mutex); +static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, + size_t count) +{ + unsigned long val; + struct pci_bus *b = NULL; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) { + mutex_lock(&pci_remove_rescan_mutex); + while ((b = pci_find_next_bus(b)) != NULL) + pci_rescan_bus(b); + mutex_unlock(&pci_remove_rescan_mutex); + } + return count; +} + +struct bus_attribute pci_bus_attrs[] = { + __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store), + __ATTR_NULL +}; +#endif + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 22dcfdb75d91..45833a5bca61 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -138,6 +138,12 @@ extern int pcie_mch_quirk; extern struct device_attribute pci_dev_attrs[]; extern struct device_attribute dev_attr_cpuaffinity; extern struct device_attribute dev_attr_cpulistaffinity; +#ifdef CONFIG_HOTPLUG +extern struct bus_attribute pci_bus_attrs[]; +#else +#define pci_bus_attrs NULL +#endif + /** * pci_match_one_device - Tell if a PCI device structure has a matching diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 60a8e5fec6c5..56c71e585f3d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1228,13 +1228,13 @@ unsigned int __devinit pci_rescan_bus(struct pci_bus *bus) max = pci_scan_child_bus(bus); - up_read(&pci_bus_sem); + down_read(&pci_bus_sem); list_for_each_entry(dev, &bus->devices, bus_list) if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) if (dev->subordinate) pci_bus_size_bridges(dev->subordinate); - down_read(&pci_bus_sem); + up_read(&pci_bus_sem); pci_bus_assign_resources(bus); pci_enable_bridges(bus); -- cgit v1.2.3-59-g8ed1b From 77c27c7b49d69d45ccb94e481653f024f1ac6650 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:36 -0600 Subject: PCI: Introduce /sys/bus/pci/devices/.../remove This patch adds an attribute named "remove" to a PCI device's sysfs directory. Writing a non-zero value to this attribute will remove the PCI device and any children of it. Trent Piepho wrote the original implementation and documentation. Thanks to Vegard Nossum for testing under kmemcheck and finding locking issues with the sysfs interface. Cc: Trent Piepho Tested-by: Vegard Nossum Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 8 ++++++++ Documentation/filesystems/sysfs-pci.txt | 10 +++++++++ drivers/pci/pci-sysfs.c | 36 +++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index e6ad047cb3c2..daa791a9e85e 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -66,6 +66,14 @@ Description: re-discover previously removed devices. Depends on CONFIG_HOTPLUG. +What: /sys/bus/pci/devices/.../remove +Date: January 2009 +Contact: Linux PCI developers +Description: + Writing a non-zero value to this attribute will + hot-remove the PCI device and any of its children. + Depends on CONFIG_HOTPLUG. + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 9f8740ca3f3b..26e4b8bc53ee 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -12,6 +12,7 @@ that support it. For example, a given bus might look like this: | |-- enable | |-- irq | |-- local_cpus + | |-- remove | |-- resource | |-- resource0 | |-- resource1 @@ -36,6 +37,7 @@ files, each with their own function. enable Whether the device is enabled (ascii, rw) irq IRQ number (ascii, ro) local_cpus nearby CPU mask (cpumask, ro) + remove remove device from kernel's list (ascii, wo) resource PCI resource host addresses (ascii, ro) resource0..N PCI resource N, if present (binary, mmap) resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap) @@ -46,6 +48,7 @@ files, each with their own function. ro - read only file rw - file is readable and writable + wo - write only file mmap - file is mmapable ascii - file contains ascii text binary - file contains binary data @@ -73,6 +76,13 @@ that the device must be enabled for a rom read to return data succesfully. In the event a driver is not bound to the device, it can be enabled using the 'enable' file, documented above. +The 'remove' file is used to remove the PCI device, by writing a non-zero +integer to the file. This does not involve any kind of hot-plug functionality, +e.g. powering off the device. The device is removed from the kernel's list of +PCI devices, the sysfs directory for it is removed, and the device will be +removed from any drivers attached to it. Removal of PCI root buses is +disallowed. + Accessing legacy resources through sysfs ---------------------------------------- diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index be7468a5eb72..e16990ecc024 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -243,6 +243,39 @@ struct bus_attribute pci_bus_attrs[] = { __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store), __ATTR_NULL }; + +static void remove_callback(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + mutex_lock(&pci_remove_rescan_mutex); + pci_remove_bus_device(pdev); + mutex_unlock(&pci_remove_rescan_mutex); +} + +static ssize_t +remove_store(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + int ret = 0; + unsigned long val; + struct pci_dev *pdev = to_pci_dev(dev); + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (pci_is_root_bus(pdev->bus)) + return -EBUSY; + + /* An attribute cannot be unregistered by one of its own methods, + * so we have to use this roundabout approach. + */ + if (val) + ret = device_schedule_callback(dev, remove_callback); + if (ret) + count = ret; + return count; +} #endif struct device_attribute pci_dev_attrs[] = { @@ -263,6 +296,9 @@ struct device_attribute pci_dev_attrs[] = { __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), broken_parity_status_show,broken_parity_status_store), __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), +#ifdef CONFIG_HOTPLUG + __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), +#endif __ATTR_NULL, }; -- cgit v1.2.3-59-g8ed1b From 738a6396c223b486304dda778119dbbca563f019 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:41 -0600 Subject: PCI: Introduce /sys/bus/pci/devices/.../rescan This interface allows the user to force a rescan of the device's parent bus and all subordinate buses, and rediscover devices removed earlier from this part of the device tree. Cc: Trent Piepho Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 10 ++++++++++ drivers/pci/pci-sysfs.c | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index daa791a9e85e..97ad190e13af 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -74,6 +74,16 @@ Description: hot-remove the PCI device and any of its children. Depends on CONFIG_HOTPLUG. +What: /sys/bus/pci/devices/.../rescan +Date: January 2009 +Contact: Linux PCI developers +Description: + Writing a non-zero value to this attribute will + force a rescan of the device's parent bus and all + child buses, and re-discover devices removed earlier + from this part of the device tree. + Depends on CONFIG_HOTPLUG. + What: /sys/bus/pci/devices/.../vpd Date: February 2008 Contact: Ben Hutchings diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index e16990ecc024..e9a8706a6401 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -244,6 +244,24 @@ struct bus_attribute pci_bus_attrs[] = { __ATTR_NULL }; +static ssize_t +dev_rescan_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + struct pci_dev *pdev = to_pci_dev(dev); + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val) { + mutex_lock(&pci_remove_rescan_mutex); + pci_rescan_bus(pdev->bus); + mutex_unlock(&pci_remove_rescan_mutex); + } + return count; +} + static void remove_callback(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -298,6 +316,7 @@ struct device_attribute pci_dev_attrs[] = { __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), #ifdef CONFIG_HOTPLUG __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), + __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), #endif __ATTR_NULL, }; -- cgit v1.2.3-59-g8ed1b From f110ca489c9b7cf3f6c9656e383e787f3aee217f Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Fri, 20 Mar 2009 14:56:56 -0600 Subject: PCI Hotplug: schedule fakephp for feature removal Now that the PCI core is capable of function-level remove and rescan as well as bus-level rescan, there's no functional need to keep fakephp anymore. We keep it around for userspace compatibility reasons, schedule removal in three years. Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- Documentation/feature-removal-schedule.txt | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 20d3b94703a4..8851eea06a02 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -335,6 +335,7 @@ Why: In 2.6.18 the Secmark concept was introduced to replace the "compat_net" Secmark, it is time to deprecate the older mechanism and start the process of removing the old code. Who: Paul Moore + --------------------------- What: sysfs ui for changing p4-clockmod parameters @@ -344,3 +345,35 @@ Why: See commits 129f8ae9b1b5be94517da76009ea956e89104ce8 and Removal is subject to fixing any remaining bugs in ACPI which may cause the thermal throttling not to happen at the right time. Who: Dave Jones , Matthew Garrett + +--------------------------- + +What: fakephp and associated sysfs files in /sys/bus/pci/slots/ +When: 2011 +Why: In 2.6.27, the semantics of /sys/bus/pci/slots was redefined to + represent a machine's physical PCI slots. The change in semantics + had userspace implications, as the hotplug core no longer allowed + drivers to create multiple sysfs files per physical slot (required + for multi-function devices, e.g.). fakephp was seen as a developer's + tool only, and its interface changed. Too late, we learned that + there were some users of the fakephp interface. + + In 2.6.30, the original fakephp interface was restored. At the same + time, the PCI core gained the ability that fakephp provided, namely + function-level hot-remove and hot-add. + + Since the PCI core now provides the same functionality, exposed in: + + /sys/bus/pci/rescan + /sys/bus/pci/devices/.../remove + /sys/bus/pci/devices/.../rescan + + there is no functional reason to maintain fakephp as well. + + We will keep the existing module so that 'modprobe fakephp' will + present the old /sys/bus/pci/slots/... interface for compatibility, + but users are urged to migrate their applications to the API above. + + After a reasonable transition period, we will remove the legacy + fakephp interface. +Who: Alex Chiang -- cgit v1.2.3-59-g8ed1b From 3a853fb933ddf9c5ea8c9f7c665dd8def26bceae Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 23 Mar 2009 18:07:47 -0700 Subject: ARM: OMAP: Add command line option for I2C bus speed, v2 This patch adds a new command line option "i2c_bus=bus_id,clkrate" into I2C bus registration helper. Purpose of the option is to override the default board specific bus speed which is supplied by the omap_register_i2c_bus. The default bus speed is typically set to speed of slowest I2C chip on the bus and overriding allow to use some experimental configurations or updated chip versions without any kernel modifications. Cc: linux-i2c@vger.kernel.org Signed-off-by: Jarkko Nikula Signed-off-by: Tony Lindgren --- Documentation/kernel-parameters.txt | 4 +++ arch/arm/plat-omap/i2c.c | 54 ++++++++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 54f21a5c262b..d775076d1563 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -830,6 +830,10 @@ and is between 256 and 4096 characters. It is defined in the file hvc_iucv= [S390] Number of z/VM IUCV hypervisor console (HVC) terminal devices. Valid values: 0..8 + i2c_bus= [HW] Override the default board specific I2C bus speed + Format: + , + i8042.debug [HW] Toggle i8042 debug mode i8042.direct [HW] Put keyboard port into non-translated mode i8042.dumbkbd [HW] Pretend that controller can only read data from diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c index 3e95954e2a87..aa70e435fa79 100644 --- a/arch/arm/plat-omap/i2c.c +++ b/arch/arm/plat-omap/i2c.c @@ -119,6 +119,46 @@ static void __init omap_i2c_mux_pins(int bus) omap_cfg_reg(scl); } +static int __init omap_i2c_nr_ports(void) +{ + int ports = 0; + + if (cpu_class_is_omap1()) + ports = 1; + else if (cpu_is_omap24xx()) + ports = 2; + else if (cpu_is_omap34xx()) + ports = 3; + + return ports; +} + +/** + * omap_i2c_bus_setup - Process command line options for the I2C bus speed + * @str: String of options + * + * This function allow to override the default I2C bus speed for given I2C + * bus with a command line option. + * + * Format: i2c_bus=bus_id,clkrate (in kHz) + * + * Returns 1 on success, 0 otherwise. + */ +static int __init omap_i2c_bus_setup(char *str) +{ + int ports; + int ints[3]; + + ports = omap_i2c_nr_ports(); + get_options(str, 3, ints); + if (ints[0] < 2 || ints[1] < 1 || ints[1] > ports) + return 0; + i2c_rate[ints[1] - 1] = ints[2]; + + return 1; +} +__setup("i2c_bus=", omap_i2c_bus_setup); + /** * omap_register_i2c_bus - register I2C bus with device descriptors * @bus_id: bus id counting from number 1 @@ -132,19 +172,12 @@ int __init omap_register_i2c_bus(int bus_id, u32 clkrate, struct i2c_board_info const *info, unsigned len) { - int ports, err; + int err; struct platform_device *pdev; struct resource *res; resource_size_t base, irq; - if (cpu_class_is_omap1()) - ports = 1; - else if (cpu_is_omap24xx()) - ports = 2; - else if (cpu_is_omap34xx()) - ports = 3; - - BUG_ON(bus_id < 1 || bus_id > ports); + BUG_ON(bus_id < 1 || bus_id > omap_i2c_nr_ports()); if (info) { err = i2c_register_board_info(bus_id, info, len); @@ -153,7 +186,8 @@ int __init omap_register_i2c_bus(int bus_id, u32 clkrate, } pdev = &omap_i2c_devices[bus_id - 1]; - *(u32 *)pdev->dev.platform_data = clkrate; + if (i2c_rate[bus_id - 1] == 0) + i2c_rate[bus_id - 1] = clkrate; if (bus_id == 1) { res = pdev->resource; -- cgit v1.2.3-59-g8ed1b From 7954763bb95fd484a76d1151b40956fe331d23b9 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 23 Mar 2009 18:07:48 -0700 Subject: ARM: OMAP: Add method to register additional I2C busses on the command line, v2 This patch extends command line option "i2c_bus=bus_id,clkrate" so that it allow to register additional I2C busses that are not registered with omap_register_i2c_bus from board initialization code. Purpose of this is to register additional board busses which are routed to external connectors only without any on board I2C devices. Cc: linux-i2c@vger.kernel.org Signed-off-by: Jarkko Nikula Signed-off-by: Tony Lindgren --- Documentation/kernel-parameters.txt | 2 + arch/arm/plat-omap/i2c.c | 73 ++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 21 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index d775076d1563..ef9827f7c84e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -831,6 +831,8 @@ and is between 256 and 4096 characters. It is defined in the file terminal devices. Valid values: 0..8 i2c_bus= [HW] Override the default board specific I2C bus speed + or register an additional I2C bus that is not + registered from board initialization code. Format: , diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c index aa70e435fa79..a303071d5e36 100644 --- a/arch/arm/plat-omap/i2c.c +++ b/arch/arm/plat-omap/i2c.c @@ -98,6 +98,8 @@ static const int omap34xx_pins[][2] = { static const int omap34xx_pins[][2] = {}; #endif +#define OMAP_I2C_CMDLINE_SETUP (BIT(31)) + static void __init omap_i2c_mux_pins(int bus) { int scl, sda; @@ -133,6 +135,31 @@ static int __init omap_i2c_nr_ports(void) return ports; } +static int __init omap_i2c_add_bus(int bus_id) +{ + struct platform_device *pdev; + struct resource *res; + resource_size_t base, irq; + + pdev = &omap_i2c_devices[bus_id - 1]; + if (bus_id == 1) { + res = pdev->resource; + if (cpu_class_is_omap1()) { + base = OMAP1_I2C_BASE; + irq = INT_I2C; + } else { + base = OMAP2_I2C_BASE1; + irq = INT_24XX_I2C1_IRQ; + } + res[0].start = base; + res[0].end = base + OMAP_I2C_SIZE; + res[1].start = irq; + } + + omap_i2c_mux_pins(bus_id - 1); + return platform_device_register(pdev); +} + /** * omap_i2c_bus_setup - Process command line options for the I2C bus speed * @str: String of options @@ -154,11 +181,33 @@ static int __init omap_i2c_bus_setup(char *str) if (ints[0] < 2 || ints[1] < 1 || ints[1] > ports) return 0; i2c_rate[ints[1] - 1] = ints[2]; + i2c_rate[ints[1] - 1] |= OMAP_I2C_CMDLINE_SETUP; return 1; } __setup("i2c_bus=", omap_i2c_bus_setup); +/* + * Register busses defined in command line but that are not registered with + * omap_register_i2c_bus from board initialization code. + */ +static int __init omap_register_i2c_bus_cmdline(void) +{ + int i, err = 0; + + for (i = 0; i < ARRAY_SIZE(i2c_rate); i++) + if (i2c_rate[i] & OMAP_I2C_CMDLINE_SETUP) { + i2c_rate[i] &= ~OMAP_I2C_CMDLINE_SETUP; + err = omap_i2c_add_bus(i + 1); + if (err) + goto out; + } + +out: + return err; +} +subsys_initcall(omap_register_i2c_bus_cmdline); + /** * omap_register_i2c_bus - register I2C bus with device descriptors * @bus_id: bus id counting from number 1 @@ -173,9 +222,6 @@ int __init omap_register_i2c_bus(int bus_id, u32 clkrate, unsigned len) { int err; - struct platform_device *pdev; - struct resource *res; - resource_size_t base, irq; BUG_ON(bus_id < 1 || bus_id > omap_i2c_nr_ports()); @@ -185,24 +231,9 @@ int __init omap_register_i2c_bus(int bus_id, u32 clkrate, return err; } - pdev = &omap_i2c_devices[bus_id - 1]; - if (i2c_rate[bus_id - 1] == 0) + if (!i2c_rate[bus_id - 1]) i2c_rate[bus_id - 1] = clkrate; + i2c_rate[bus_id - 1] &= ~OMAP_I2C_CMDLINE_SETUP; - if (bus_id == 1) { - res = pdev->resource; - if (cpu_class_is_omap1()) { - base = OMAP1_I2C_BASE; - irq = INT_I2C; - } else { - base = OMAP2_I2C_BASE1; - irq = INT_24XX_I2C1_IRQ; - } - res[0].start = base; - res[0].end = base + OMAP_I2C_SIZE; - res[1].start = irq; - } - - omap_i2c_mux_pins(bus_id - 1); - return platform_device_register(pdev); + return omap_i2c_add_bus(bus_id); } -- cgit v1.2.3-59-g8ed1b From 471c604daf73ff549d374ee54f9e6bfd5a54d4e8 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Thu, 19 Feb 2009 22:54:45 -0700 Subject: USB: usbmon: Add binary API v1 This patch adds an extension to the binary API so it reaches parity with existing text API (so-called "1u"). The extension delivers additional data, such as ISO descriptors and the interrupt interval. Signed-Off-By: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/usbmon.txt | 27 +++++--- drivers/usb/mon/mon_bin.c | 142 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 136 insertions(+), 33 deletions(-) (limited to 'Documentation') diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index 270481906dc8..6c3c625b7f30 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -229,16 +229,26 @@ struct usbmon_packet { int status; /* 28: */ unsigned int length; /* 32: Length of data (submitted or actual) */ unsigned int len_cap; /* 36: Delivered length */ - unsigned char setup[8]; /* 40: Only for Control 'S' */ -}; /* 48 bytes total */ + union { /* 40: */ + unsigned char setup[SETUP_LEN]; /* Only for Control S-type */ + struct iso_rec { /* Only for ISO */ + int error_count; + int numdesc; + } iso; + } s; + int interval; /* 48: Only for Interrupt and ISO */ + int start_frame; /* 52: For ISO */ + unsigned int xfer_flags; /* 56: copy of URB's transfer_flags */ + unsigned int ndesc; /* 60: Actual number of ISO descriptors */ +}; /* 64 total length */ These events can be received from a character device by reading with read(2), -with an ioctl(2), or by accessing the buffer with mmap. +with an ioctl(2), or by accessing the buffer with mmap. However, read(2) +only returns first 48 bytes for compatibility reasons. The character device is usually called /dev/usbmonN, where N is the USB bus number. Number zero (/dev/usbmon0) is special and means "all buses". -However, this feature is not implemented yet. Note that specific naming -policy is set by your Linux distribution. +Note that specific naming policy is set by your Linux distribution. If you create /dev/usbmon0 by hand, make sure that it is owned by root and has mode 0600. Otherwise, unpriviledged users will be able to snoop @@ -279,9 +289,10 @@ size is out of [unspecified] bounds for this kernel, the call fails with This call returns the current size of the buffer in bytes. MON_IOCX_GET, defined as _IOW(MON_IOC_MAGIC, 6, struct mon_get_arg) + MON_IOCX_GETX, defined as _IOW(MON_IOC_MAGIC, 10, struct mon_get_arg) -This call waits for events to arrive if none were in the kernel buffer, -then returns the first event. Its argument is a pointer to the following +These calls wait for events to arrive if none were in the kernel buffer, +then return the first event. The argument is a pointer to the following structure: struct mon_get_arg { @@ -294,6 +305,8 @@ Before the call, hdr, data, and alloc should be filled. Upon return, the area pointed by hdr contains the next event structure, and the data buffer contains the data, if any. The event is removed from the kernel buffer. +The MON_IOCX_GET copies 48 bytes, MON_IOCX_GETX copies 64 bytes. + MON_IOCX_MFETCH, defined as _IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg) This ioctl is primarily used when the application accesses the buffer diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 4cf27c72423e..f8d9045d668a 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -37,10 +37,13 @@ #define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get) #define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch) #define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8) +/* #9 was MON_IOCT_SETAPI */ +#define MON_IOCX_GETX _IOW(MON_IOC_MAGIC, 10, struct mon_bin_get) #ifdef CONFIG_COMPAT #define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32) #define MON_IOCX_MFETCH32 _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch32) +#define MON_IOCX_GETX32 _IOW(MON_IOC_MAGIC, 10, struct mon_bin_get32) #endif /* @@ -92,7 +95,29 @@ struct mon_bin_hdr { int status; unsigned int len_urb; /* Length of data (submitted or actual) */ unsigned int len_cap; /* Delivered length */ - unsigned char setup[SETUP_LEN]; /* Only for Control S-type */ + union { + unsigned char setup[SETUP_LEN]; /* Only for Control S-type */ + struct iso_rec { + int error_count; + int numdesc; + } iso; + } s; + int interval; + int start_frame; + unsigned int xfer_flags; + unsigned int ndesc; /* Actual number of ISO descriptors */ +}; + +/* + * ISO vector, packed into the head of data stream. + * This has to take 16 bytes to make sure that the end of buffer + * wrap is not happening in the middle of a descriptor. + */ +struct mon_bin_isodesc { + int iso_status; + unsigned int iso_off; + unsigned int iso_len; + u32 _pad; }; /* per file statistic */ @@ -102,7 +127,7 @@ struct mon_bin_stats { }; struct mon_bin_get { - struct mon_bin_hdr __user *hdr; /* Only 48 bytes, not 64. */ + struct mon_bin_hdr __user *hdr; /* Can be 48 bytes or 64. */ void __user *data; size_t alloc; /* Length of data (can be zero) */ }; @@ -131,6 +156,11 @@ struct mon_bin_mfetch32 { #define PKT_ALIGN 64 #define PKT_SIZE 64 +#define PKT_SZ_API0 48 /* API 0 (2.6.20) size */ +#define PKT_SZ_API1 64 /* API 1 size: extra fields */ + +#define ISODESC_MAX 128 /* Same number as usbfs allows, 2048 bytes. */ + /* max number of USB bus supported */ #define MON_BIN_MAX_MINOR 128 @@ -360,12 +390,8 @@ static inline char mon_bin_get_setup(unsigned char *setupb, const struct urb *urb, char ev_type) { - if (!usb_endpoint_xfer_control(&urb->ep->desc) || ev_type != 'S') - return '-'; - if (urb->setup_packet == NULL) return 'Z'; - memcpy(setupb, urb->setup_packet, SETUP_LEN); return 0; } @@ -387,6 +413,26 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp, return 0; } +static void mon_bin_get_isodesc(const struct mon_reader_bin *rp, + unsigned int offset, struct urb *urb, char ev_type, unsigned int ndesc) +{ + struct mon_bin_isodesc *dp; + struct usb_iso_packet_descriptor *fp; + + fp = urb->iso_frame_desc; + while (ndesc-- != 0) { + dp = (struct mon_bin_isodesc *) + (rp->b_vec[offset / CHUNK_SIZE].ptr + offset % CHUNK_SIZE); + dp->iso_status = fp->status; + dp->iso_off = fp->offset; + dp->iso_len = (ev_type == 'S') ? fp->length : fp->actual_length; + dp->_pad = 0; + if ((offset += sizeof(struct mon_bin_isodesc)) >= rp->b_size) + offset = 0; + fp++; + } +} + static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, char ev_type, int status) { @@ -396,6 +442,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, unsigned int urb_length; unsigned int offset; unsigned int length; + unsigned int ndesc, lendesc; unsigned char dir; struct mon_bin_hdr *ep; char data_tag = 0; @@ -407,6 +454,19 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, /* * Find the maximum allowable length, then allocate space. */ + if (usb_endpoint_xfer_isoc(epd)) { + if (urb->number_of_packets < 0) { + ndesc = 0; + } else if (urb->number_of_packets >= ISODESC_MAX) { + ndesc = ISODESC_MAX; + } else { + ndesc = urb->number_of_packets; + } + } else { + ndesc = 0; + } + lendesc = ndesc*sizeof(struct mon_bin_isodesc); + urb_length = (ev_type == 'S') ? urb->transfer_buffer_length : urb->actual_length; length = urb_length; @@ -429,10 +489,12 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, dir = 0; } - if (rp->mmap_active) - offset = mon_buff_area_alloc_contiguous(rp, length + PKT_SIZE); - else - offset = mon_buff_area_alloc(rp, length + PKT_SIZE); + if (rp->mmap_active) { + offset = mon_buff_area_alloc_contiguous(rp, + length + PKT_SIZE + lendesc); + } else { + offset = mon_buff_area_alloc(rp, length + PKT_SIZE + lendesc); + } if (offset == ~0) { rp->cnt_lost++; spin_unlock_irqrestore(&rp->b_lock, flags); @@ -456,9 +518,31 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, ep->ts_usec = ts.tv_usec; ep->status = status; ep->len_urb = urb_length; - ep->len_cap = length; + ep->len_cap = length + lendesc; + ep->xfer_flags = urb->transfer_flags; + + if (usb_endpoint_xfer_int(epd)) { + ep->interval = urb->interval; + } else if (usb_endpoint_xfer_isoc(epd)) { + ep->interval = urb->interval; + ep->start_frame = urb->start_frame; + ep->s.iso.error_count = urb->error_count; + ep->s.iso.numdesc = urb->number_of_packets; + } + + if (usb_endpoint_xfer_control(epd) && ev_type == 'S') { + ep->flag_setup = mon_bin_get_setup(ep->s.setup, urb, ev_type); + } else { + ep->flag_setup = '-'; + } + + if (ndesc != 0) { + ep->ndesc = ndesc; + mon_bin_get_isodesc(rp, offset, urb, ev_type, ndesc); + if ((offset += lendesc) >= rp->b_size) + offset -= rp->b_size; + } - ep->flag_setup = mon_bin_get_setup(ep->setup, urb, ev_type); if (length != 0) { ep->flag_data = mon_bin_get_data(rp, offset, urb, length); if (ep->flag_data != 0) { /* Yes, it's 0x00, not '0' */ @@ -592,7 +676,8 @@ err_alloc: * Returns zero or error. */ static int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp, - struct mon_bin_hdr __user *hdr, void __user *data, unsigned int nbytes) + struct mon_bin_hdr __user *hdr, unsigned int hdrbytes, + void __user *data, unsigned int nbytes) { unsigned long flags; struct mon_bin_hdr *ep; @@ -609,7 +694,7 @@ static int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp, ep = MON_OFF2HDR(rp, rp->b_out); - if (copy_to_user(hdr, ep, sizeof(struct mon_bin_hdr))) { + if (copy_to_user(hdr, ep, hdrbytes)) { mutex_unlock(&rp->fetch_lock); return -EFAULT; } @@ -657,6 +742,7 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct mon_reader_bin *rp = file->private_data; + unsigned int hdrbytes = PKT_SZ_API0; unsigned long flags; struct mon_bin_hdr *ep; unsigned int offset; @@ -674,8 +760,8 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, ep = MON_OFF2HDR(rp, rp->b_out); - if (rp->b_read < sizeof(struct mon_bin_hdr)) { - step_len = min(nbytes, sizeof(struct mon_bin_hdr) - rp->b_read); + if (rp->b_read < hdrbytes) { + step_len = min(nbytes, (size_t)(hdrbytes - rp->b_read)); ptr = ((char *)ep) + rp->b_read; if (step_len && copy_to_user(buf, ptr, step_len)) { mutex_unlock(&rp->fetch_lock); @@ -687,13 +773,13 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, done += step_len; } - if (rp->b_read >= sizeof(struct mon_bin_hdr)) { + if (rp->b_read >= hdrbytes) { step_len = ep->len_cap; - step_len -= rp->b_read - sizeof(struct mon_bin_hdr); + step_len -= rp->b_read - hdrbytes; if (step_len > nbytes) step_len = nbytes; offset = rp->b_out + PKT_SIZE; - offset += rp->b_read - sizeof(struct mon_bin_hdr); + offset += rp->b_read - hdrbytes; if (offset >= rp->b_size) offset -= rp->b_size; if (copy_from_buf(rp, offset, buf, step_len)) { @@ -709,7 +795,7 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, /* * Check if whole packet was read, and if so, jump to the next one. */ - if (rp->b_read >= sizeof(struct mon_bin_hdr) + ep->len_cap) { + if (rp->b_read >= hdrbytes + ep->len_cap) { spin_lock_irqsave(&rp->b_lock, flags); mon_buff_area_free(rp, PKT_SIZE + ep->len_cap); spin_unlock_irqrestore(&rp->b_lock, flags); @@ -908,6 +994,7 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, break; case MON_IOCX_GET: + case MON_IOCX_GETX: { struct mon_bin_get getb; @@ -917,8 +1004,9 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, if (getb.alloc > 0x10000000) /* Want to cast to u32 */ return -EINVAL; - ret = mon_bin_get_event(file, rp, - getb.hdr, getb.data, (unsigned int)getb.alloc); + ret = mon_bin_get_event(file, rp, getb.hdr, + (cmd == MON_IOCX_GET)? PKT_SZ_API0: PKT_SZ_API1, + getb.data, (unsigned int)getb.alloc); } break; @@ -984,16 +1072,18 @@ static long mon_bin_compat_ioctl(struct file *file, switch (cmd) { - case MON_IOCX_GET32: { + case MON_IOCX_GET32: + case MON_IOCX_GETX32: + { struct mon_bin_get32 getb; if (copy_from_user(&getb, (void __user *)arg, sizeof(struct mon_bin_get32))) return -EFAULT; - ret = mon_bin_get_event(file, rp, - compat_ptr(getb.hdr32), compat_ptr(getb.data32), - getb.alloc32); + ret = mon_bin_get_event(file, rp, compat_ptr(getb.hdr32), + (cmd == MON_IOCX_GET32)? PKT_SZ_API0: PKT_SZ_API1, + compat_ptr(getb.data32), getb.alloc32); if (ret < 0) return ret; } -- cgit v1.2.3-59-g8ed1b From 8205779114e8f612549d191f8e151526a74ab9f2 Mon Sep 17 00:00:00 2001 From: "Hans J. Koch" Date: Wed, 7 Jan 2009 00:15:39 +0100 Subject: UIO: Add name attributes for mappings and port regions If a UIO device has several memory mappings, it can be difficult for userspace to find the right one. The situation becomes even worse if the UIO driver can handle different versions of a card that have different numbers of mappings. Benedikt Spranger has such cards and pointed this out to me. Thanks, Bene! To address this problem, this patch adds "name" sysfs attributes for each mapping. Userspace can use these to clearly identify each mapping. The name string is optional. If a driver doesn't set it, an empty string will be returned, so this patch won't break existing drivers. The same problem exists for port region information, so a "name" attribute is added there, too. Signed-off-by: Hans J. Koch Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/uio-howto.tmpl | 29 +++++++++++++++++++++++++---- drivers/uio/uio.c | 22 ++++++++++++++++++++++ include/linux/uio_driver.h | 4 ++++ 3 files changed, 51 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index 52e1b79ce0e6..8f6e3b2403c7 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl @@ -41,6 +41,13 @@ GPL version 2. + + 0.8 + 2008-12-24 + hjk + Added name attributes in mem and portio sysfs directories. + + 0.7 2008-12-23 @@ -303,10 +310,17 @@ interested in translating it, please email me appear if the size of the mapping is not 0.
- Each mapX/ directory contains two read-only files - that show start address and size of the memory: + Each mapX/ directory contains four read-only files + that show attributes of the memory: + + + name: A string identifier for this mapping. This + is optional, the string can be empty. Drivers can set this to make it + easier for userspace to find the correct mapping. + + addr: The address of memory that can be mapped. @@ -366,10 +380,17 @@ offset = N * getpagesize(); /sys/class/uio/uioX/portio/. - Each portX/ directory contains three read-only - files that show start, size, and type of the port region: + Each portX/ directory contains four read-only + files that show name, start, size, and type of the port region: + + + name: A string identifier for this port region. + The string is optional and can be empty. Drivers can set it to make it + easier for userspace to find a certain port region. + + start: The first port of this region. diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 4ca85a113aa2..68a496557788 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -61,6 +61,14 @@ struct uio_map { }; #define to_map(map) container_of(map, struct uio_map, kobj) +static ssize_t map_name_show(struct uio_mem *mem, char *buf) +{ + if (unlikely(!mem->name)) + mem->name = ""; + + return sprintf(buf, "%s\n", mem->name); +} + static ssize_t map_addr_show(struct uio_mem *mem, char *buf) { return sprintf(buf, "0x%lx\n", mem->addr); @@ -82,6 +90,8 @@ struct map_sysfs_entry { ssize_t (*store)(struct uio_mem *, const char *, size_t); }; +static struct map_sysfs_entry name_attribute = + __ATTR(name, S_IRUGO, map_name_show, NULL); static struct map_sysfs_entry addr_attribute = __ATTR(addr, S_IRUGO, map_addr_show, NULL); static struct map_sysfs_entry size_attribute = @@ -90,6 +100,7 @@ static struct map_sysfs_entry offset_attribute = __ATTR(offset, S_IRUGO, map_offset_show, NULL); static struct attribute *attrs[] = { + &name_attribute.attr, &addr_attribute.attr, &size_attribute.attr, &offset_attribute.attr, @@ -133,6 +144,14 @@ struct uio_portio { }; #define to_portio(portio) container_of(portio, struct uio_portio, kobj) +static ssize_t portio_name_show(struct uio_port *port, char *buf) +{ + if (unlikely(!port->name)) + port->name = ""; + + return sprintf(buf, "%s\n", port->name); +} + static ssize_t portio_start_show(struct uio_port *port, char *buf) { return sprintf(buf, "0x%lx\n", port->start); @@ -159,6 +178,8 @@ struct portio_sysfs_entry { ssize_t (*store)(struct uio_port *, const char *, size_t); }; +static struct portio_sysfs_entry portio_name_attribute = + __ATTR(name, S_IRUGO, portio_name_show, NULL); static struct portio_sysfs_entry portio_start_attribute = __ATTR(start, S_IRUGO, portio_start_show, NULL); static struct portio_sysfs_entry portio_size_attribute = @@ -167,6 +188,7 @@ static struct portio_sysfs_entry portio_porttype_attribute = __ATTR(porttype, S_IRUGO, portio_porttype_show, NULL); static struct attribute *portio_attrs[] = { + &portio_name_attribute.attr, &portio_start_attribute.attr, &portio_size_attribute.attr, &portio_porttype_attribute.attr, diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index a0bb6bd2e5c1..5dcc9ff72f69 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -22,6 +22,7 @@ struct uio_map; /** * struct uio_mem - description of a UIO memory region + * @name: name of the memory region for identification * @addr: address of the device's memory * @size: size of IO * @memtype: type of memory addr points to @@ -29,6 +30,7 @@ struct uio_map; * @map: for use by the UIO core only. */ struct uio_mem { + const char *name; unsigned long addr; unsigned long size; int memtype; @@ -42,12 +44,14 @@ struct uio_portio; /** * struct uio_port - description of a UIO port region + * @name: name of the port region for identification * @start: start of port region * @size: size of port region * @porttype: type of port (see UIO_PORT_* below) * @portio: for use by the UIO core only. */ struct uio_port { + const char *name; unsigned long start; unsigned long size; int porttype; -- cgit v1.2.3-59-g8ed1b From e9d376f0fa66bd630fe27403669c6ae6c22a868f Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Thu, 5 Feb 2009 11:51:38 -0500 Subject: dynamic debug: combine dprintk and dynamic printk This patch combines Greg Bank's dprintk() work with the existing dynamic printk patchset, we are now calling it 'dynamic debug'. The new feature of this patchset is a richer /debugfs control file interface, (an example output from my system is at the bottom), which allows fined grained control over the the debug output. The output can be controlled by function, file, module, format string, and line number. for example, enabled all debug messages in module 'nf_conntrack': echo -n 'module nf_conntrack +p' > /mnt/debugfs/dynamic_debug/control to disable them: echo -n 'module nf_conntrack -p' > /mnt/debugfs/dynamic_debug/control A further explanation can be found in the documentation patch. Signed-off-by: Greg Banks Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 5 - include/asm-generic/vmlinux.lds.h | 15 +- include/linux/device.h | 2 +- include/linux/dynamic_debug.h | 88 +++++ include/linux/dynamic_printk.h | 93 ----- include/linux/kernel.h | 4 +- kernel/module.c | 25 +- lib/Kconfig.debug | 2 +- lib/Makefile | 2 +- lib/dynamic_debug.c | 756 ++++++++++++++++++++++++++++++++++++ lib/dynamic_printk.c | 414 -------------------- net/netfilter/nf_conntrack_pptp.c | 2 +- scripts/Makefile.lib | 2 +- 13 files changed, 867 insertions(+), 543 deletions(-) create mode 100644 include/linux/dynamic_debug.h delete mode 100644 include/linux/dynamic_printk.h create mode 100644 lib/dynamic_debug.c delete mode 100644 lib/dynamic_printk.c (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 54f21a5c262b..3a1aa8a4affc 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1816,11 +1816,6 @@ and is between 256 and 4096 characters. It is defined in the file autoconfiguration. Ranges are in pairs (memory base and size). - dynamic_printk Enables pr_debug()/dev_dbg() calls if - CONFIG_DYNAMIC_PRINTK_DEBUG has been enabled. - These can also be switched on/off via - /dynamic_printk/modules - print-fatal-signals= [KNL] debug: print fatal signals print-fatal-signals=1: print segfault info to diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index c61fab1dd2f8..aca40b93bd28 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -80,6 +80,11 @@ VMLINUX_SYMBOL(__start___tracepoints) = .; \ *(__tracepoints) \ VMLINUX_SYMBOL(__stop___tracepoints) = .; \ + /* implement dynamic printk debug */ \ + . = ALIGN(8); \ + VMLINUX_SYMBOL(__start___verbose) = .; \ + *(__verbose) \ + VMLINUX_SYMBOL(__stop___verbose) = .; \ LIKELY_PROFILE() \ BRANCH_PROFILE() @@ -309,15 +314,7 @@ CPU_DISCARD(init.data) \ CPU_DISCARD(init.rodata) \ MEM_DISCARD(init.data) \ - MEM_DISCARD(init.rodata) \ - /* implement dynamic printk debug */ \ - VMLINUX_SYMBOL(__start___verbose_strings) = .; \ - *(__verbose_strings) \ - VMLINUX_SYMBOL(__stop___verbose_strings) = .; \ - . = ALIGN(8); \ - VMLINUX_SYMBOL(__start___verbose) = .; \ - *(__verbose) \ - VMLINUX_SYMBOL(__stop___verbose) = .; + MEM_DISCARD(init.rodata) #define INIT_TEXT \ *(.init.text) \ diff --git a/include/linux/device.h b/include/linux/device.h index f98d0cfb4f81..2918c0e8fdfd 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -582,7 +582,7 @@ extern const char *dev_driver_string(const struct device *dev); #if defined(DEBUG) #define dev_dbg(dev, format, arg...) \ dev_printk(KERN_DEBUG , dev , format , ## arg) -#elif defined(CONFIG_DYNAMIC_PRINTK_DEBUG) +#elif defined(CONFIG_DYNAMIC_DEBUG) #define dev_dbg(dev, format, ...) do { \ dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ } while (0) diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h new file mode 100644 index 000000000000..07781aaa1164 --- /dev/null +++ b/include/linux/dynamic_debug.h @@ -0,0 +1,88 @@ +#ifndef _DYNAMIC_DEBUG_H +#define _DYNAMIC_DEBUG_H + +/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which + * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They + * use independent hash functions, to reduce the chance of false positives. + */ +extern long long dynamic_debug_enabled; +extern long long dynamic_debug_enabled2; + +/* + * An instance of this structure is created in a special + * ELF section at every dynamic debug callsite. At runtime, + * the special section is treated as an array of these. + */ +struct _ddebug { + /* + * These fields are used to drive the user interface + * for selecting and displaying debug callsites. + */ + const char *modname; + const char *function; + const char *filename; + const char *format; + char primary_hash; + char secondary_hash; + unsigned int lineno:24; + /* + * The flags field controls the behaviour at the callsite. + * The bits here are changed dynamically when the user + * writes commands to /dynamic_debug/ddebug + */ +#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */ +#define _DPRINTK_FLAGS_DEFAULT 0 + unsigned int flags:8; +} __attribute__((aligned(8))); + + +int ddebug_add_module(struct _ddebug *tab, unsigned int n, + const char *modname); + +#if defined(CONFIG_DYNAMIC_DEBUG) +extern int ddebug_remove_module(char *mod_name); + +#define __dynamic_dbg_enabled(dd) ({ \ + int __ret = 0; \ + if (unlikely((dynamic_debug_enabled & (1LL << DEBUG_HASH)) && \ + (dynamic_debug_enabled2 & (1LL << DEBUG_HASH2)))) \ + if (unlikely(dd.flags)) \ + __ret = 1; \ + __ret; }) + +#define dynamic_pr_debug(fmt, ...) do { \ + static struct _ddebug descriptor \ + __used \ + __attribute__((section("__verbose"), aligned(8))) = \ + { KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \ + DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \ + if (__dynamic_dbg_enabled(descriptor)) \ + printk(KERN_DEBUG KBUILD_MODNAME ":" fmt, \ + ##__VA_ARGS__); \ + } while (0) + + +#define dynamic_dev_dbg(dev, fmt, ...) do { \ + static struct _ddebug descriptor \ + __used \ + __attribute__((section("__verbose"), aligned(8))) = \ + { KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \ + DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \ + if (__dynamic_dbg_enabled(descriptor)) \ + dev_printk(KERN_DEBUG, dev, \ + KBUILD_MODNAME ": " fmt, \ + ##__VA_ARGS__); \ + } while (0) + +#else + +static inline int ddebug_remove_module(char *mod) +{ + return 0; +} + +#define dynamic_pr_debug(fmt, ...) do { } while (0) +#define dynamic_dev_dbg(dev, format, ...) do { } while (0) +#endif + +#endif diff --git a/include/linux/dynamic_printk.h b/include/linux/dynamic_printk.h deleted file mode 100644 index 2d528d009074..000000000000 --- a/include/linux/dynamic_printk.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef _DYNAMIC_PRINTK_H -#define _DYNAMIC_PRINTK_H - -#define DYNAMIC_DEBUG_HASH_BITS 6 -#define DEBUG_HASH_TABLE_SIZE (1 << DYNAMIC_DEBUG_HASH_BITS) - -#define TYPE_BOOLEAN 1 - -#define DYNAMIC_ENABLED_ALL 0 -#define DYNAMIC_ENABLED_NONE 1 -#define DYNAMIC_ENABLED_SOME 2 - -extern int dynamic_enabled; - -/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which - * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They - * use independent hash functions, to reduce the chance of false positives. - */ -extern long long dynamic_printk_enabled; -extern long long dynamic_printk_enabled2; - -struct mod_debug { - char *modname; - char *logical_modname; - char *flag_names; - int type; - int hash; - int hash2; -} __attribute__((aligned(8))); - -int register_dynamic_debug_module(char *mod_name, int type, char *share_name, - char *flags, int hash, int hash2); - -#if defined(CONFIG_DYNAMIC_PRINTK_DEBUG) -extern int unregister_dynamic_debug_module(char *mod_name); -extern int __dynamic_dbg_enabled_helper(char *modname, int type, - int value, int hash); - -#define __dynamic_dbg_enabled(module, type, value, level, hash) ({ \ - int __ret = 0; \ - if (unlikely((dynamic_printk_enabled & (1LL << DEBUG_HASH)) && \ - (dynamic_printk_enabled2 & (1LL << DEBUG_HASH2)))) \ - __ret = __dynamic_dbg_enabled_helper(module, type, \ - value, hash);\ - __ret; }) - -#define dynamic_pr_debug(fmt, ...) do { \ - static char mod_name[] \ - __attribute__((section("__verbose_strings"))) \ - = KBUILD_MODNAME; \ - static struct mod_debug descriptor \ - __used \ - __attribute__((section("__verbose"), aligned(8))) = \ - { mod_name, mod_name, NULL, TYPE_BOOLEAN, DEBUG_HASH, DEBUG_HASH2 };\ - if (__dynamic_dbg_enabled(KBUILD_MODNAME, TYPE_BOOLEAN, \ - 0, 0, DEBUG_HASH)) \ - printk(KERN_DEBUG KBUILD_MODNAME ":" fmt, \ - ##__VA_ARGS__); \ - } while (0) - -#define dynamic_dev_dbg(dev, format, ...) do { \ - static char mod_name[] \ - __attribute__((section("__verbose_strings"))) \ - = KBUILD_MODNAME; \ - static struct mod_debug descriptor \ - __used \ - __attribute__((section("__verbose"), aligned(8))) = \ - { mod_name, mod_name, NULL, TYPE_BOOLEAN, DEBUG_HASH, DEBUG_HASH2 };\ - if (__dynamic_dbg_enabled(KBUILD_MODNAME, TYPE_BOOLEAN, \ - 0, 0, DEBUG_HASH)) \ - dev_printk(KERN_DEBUG, dev, \ - KBUILD_MODNAME ": " format, \ - ##__VA_ARGS__); \ - } while (0) - -#else - -static inline int unregister_dynamic_debug_module(const char *mod_name) -{ - return 0; -} -static inline int __dynamic_dbg_enabled_helper(char *modname, int type, - int value, int hash) -{ - return 0; -} - -#define __dynamic_dbg_enabled(module, type, value, level, hash) ({ 0; }) -#define dynamic_pr_debug(fmt, ...) do { } while (0) -#define dynamic_dev_dbg(dev, format, ...) do { } while (0) -#endif - -#endif diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 7fa371898e3e..b5496ecbec71 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include @@ -358,7 +358,7 @@ static inline char *pack_hex_byte(char *buf, u8 byte) #if defined(DEBUG) #define pr_debug(fmt, ...) \ printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) -#elif defined(CONFIG_DYNAMIC_PRINTK_DEBUG) +#elif defined(CONFIG_DYNAMIC_DEBUG) #define pr_debug(fmt, ...) do { \ dynamic_pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ } while (0) diff --git a/kernel/module.c b/kernel/module.c index 1196f5d11700..77672233387f 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -822,7 +822,7 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user, mutex_lock(&module_mutex); /* Store the name of the last unloaded module for diagnostic purposes */ strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module)); - unregister_dynamic_debug_module(mod->name); + ddebug_remove_module(mod->name); free_module(mod); out: @@ -1827,19 +1827,13 @@ static inline void add_kallsyms(struct module *mod, } #endif /* CONFIG_KALLSYMS */ -static void dynamic_printk_setup(struct mod_debug *debug, unsigned int num) +static void dynamic_debug_setup(struct _ddebug *debug, unsigned int num) { -#ifdef CONFIG_DYNAMIC_PRINTK_DEBUG - unsigned int i; - - for (i = 0; i < num; i++) { - register_dynamic_debug_module(debug[i].modname, - debug[i].type, - debug[i].logical_modname, - debug[i].flag_names, - debug[i].hash, debug[i].hash2); - } -#endif /* CONFIG_DYNAMIC_PRINTK_DEBUG */ +#ifdef CONFIG_DYNAMIC_DEBUG + if (ddebug_add_module(debug, num, debug->modname)) + printk(KERN_ERR "dynamic debug error adding module: %s\n", + debug->modname); +#endif } static void *module_alloc_update_bounds(unsigned long size) @@ -2213,12 +2207,13 @@ static noinline struct module *load_module(void __user *umod, add_kallsyms(mod, sechdrs, symindex, strindex, secstrings); if (!mod->taints) { - struct mod_debug *debug; + struct _ddebug *debug; unsigned int num_debug; debug = section_objs(hdr, sechdrs, secstrings, "__verbose", sizeof(*debug), &num_debug); - dynamic_printk_setup(debug, num_debug); + if (debug) + dynamic_debug_setup(debug, num_debug); } /* sechdrs[0].sh_size is always zero */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 1bcf9cd4baa0..0dd1c04c7323 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -847,7 +847,7 @@ config BUILD_DOCSRC Say N if you are unsure. -config DYNAMIC_PRINTK_DEBUG +config DYNAMIC_DEBUG bool "Enable dynamic printk() call support" default n depends on PRINTK diff --git a/lib/Makefile b/lib/Makefile index 32b0e64ded27..8633d6be9d21 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -82,7 +82,7 @@ obj-$(CONFIG_HAVE_LMB) += lmb.o obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o -obj-$(CONFIG_DYNAMIC_PRINTK_DEBUG) += dynamic_printk.o +obj-$(CONFIG_DYNAMIC_DEBUG) += dynamic_debug.o hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c new file mode 100644 index 000000000000..9e123ae326bc --- /dev/null +++ b/lib/dynamic_debug.c @@ -0,0 +1,756 @@ +/* + * lib/dynamic_debug.c + * + * make pr_debug()/dev_dbg() calls runtime configurable based upon their + * source module. + * + * Copyright (C) 2008 Jason Baron + * By Greg Banks + * Copyright (c) 2008 Silicon Graphics Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct _ddebug __start___verbose[]; +extern struct _ddebug __stop___verbose[]; + +/* dynamic_debug_enabled, and dynamic_debug_enabled2 are bitmasks in which + * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They + * use independent hash functions, to reduce the chance of false positives. + */ +long long dynamic_debug_enabled; +EXPORT_SYMBOL_GPL(dynamic_debug_enabled); +long long dynamic_debug_enabled2; +EXPORT_SYMBOL_GPL(dynamic_debug_enabled2); + +struct ddebug_table { + struct list_head link; + char *mod_name; + unsigned int num_ddebugs; + unsigned int num_enabled; + struct _ddebug *ddebugs; +}; + +struct ddebug_query { + const char *filename; + const char *module; + const char *function; + const char *format; + unsigned int first_lineno, last_lineno; +}; + +struct ddebug_iter { + struct ddebug_table *table; + unsigned int idx; +}; + +static DEFINE_MUTEX(ddebug_lock); +static LIST_HEAD(ddebug_tables); +static int verbose = 0; + +/* Return the last part of a pathname */ +static inline const char *basename(const char *path) +{ + const char *tail = strrchr(path, '/'); + return tail ? tail+1 : path; +} + +/* format a string into buf[] which describes the _ddebug's flags */ +static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, + size_t maxlen) +{ + char *p = buf; + + BUG_ON(maxlen < 4); + if (dp->flags & _DPRINTK_FLAGS_PRINT) + *p++ = 'p'; + if (p == buf) + *p++ = '-'; + *p = '\0'; + + return buf; +} + +/* + * must be called with ddebug_lock held + */ + +static int disabled_hash(char hash, bool first_table) +{ + struct ddebug_table *dt; + char table_hash_value; + + list_for_each_entry(dt, &ddebug_tables, link) { + if (first_table) + table_hash_value = dt->ddebugs->primary_hash; + else + table_hash_value = dt->ddebugs->secondary_hash; + if (dt->num_enabled && (hash == table_hash_value)) + return 0; + } + return 1; +} + +/* + * Search the tables for _ddebug's which match the given + * `query' and apply the `flags' and `mask' to them. Tells + * the user which ddebug's were changed, or whether none + * were matched. + */ +static void ddebug_change(const struct ddebug_query *query, + unsigned int flags, unsigned int mask) +{ + int i; + struct ddebug_table *dt; + unsigned int newflags; + unsigned int nfound = 0; + char flagbuf[8]; + + /* search for matching ddebugs */ + mutex_lock(&ddebug_lock); + list_for_each_entry(dt, &ddebug_tables, link) { + + /* match against the module name */ + if (query->module != NULL && + strcmp(query->module, dt->mod_name)) + continue; + + for (i = 0 ; i < dt->num_ddebugs ; i++) { + struct _ddebug *dp = &dt->ddebugs[i]; + + /* match against the source filename */ + if (query->filename != NULL && + strcmp(query->filename, dp->filename) && + strcmp(query->filename, basename(dp->filename))) + continue; + + /* match against the function */ + if (query->function != NULL && + strcmp(query->function, dp->function)) + continue; + + /* match against the format */ + if (query->format != NULL && + strstr(dp->format, query->format) == NULL) + continue; + + /* match against the line number range */ + if (query->first_lineno && + dp->lineno < query->first_lineno) + continue; + if (query->last_lineno && + dp->lineno > query->last_lineno) + continue; + + nfound++; + + newflags = (dp->flags & mask) | flags; + if (newflags == dp->flags) + continue; + + if (!newflags) + dt->num_enabled--; + else if (!dp-flags) + dt->num_enabled++; + dp->flags = newflags; + if (newflags) { + dynamic_debug_enabled |= + (1LL << dp->primary_hash); + dynamic_debug_enabled2 |= + (1LL << dp->secondary_hash); + } else { + if (disabled_hash(dp->primary_hash, true)) + dynamic_debug_enabled &= + ~(1LL << dp->primary_hash); + if (disabled_hash(dp->secondary_hash, false)) + dynamic_debug_enabled2 &= + ~(1LL << dp->secondary_hash); + } + if (verbose) + printk(KERN_INFO + "ddebug: changed %s:%d [%s]%s %s\n", + dp->filename, dp->lineno, + dt->mod_name, dp->function, + ddebug_describe_flags(dp, flagbuf, + sizeof(flagbuf))); + } + } + mutex_unlock(&ddebug_lock); + + if (!nfound && verbose) + printk(KERN_INFO "ddebug: no matches for query\n"); +} + +/* + * Wrapper around strsep() to collapse the multiple empty tokens + * that it returns when fed sequences of separator characters. + * Now, if we had strtok_r()... + */ +static inline char *nearly_strtok_r(char **p, const char *sep) +{ + char *r; + + while ((r = strsep(p, sep)) != NULL && *r == '\0') + ; + return r; +} + +/* + * Split the buffer `buf' into space-separated words. + * Return the number of such words or <0 on error. + */ +static int ddebug_tokenize(char *buf, char *words[], int maxwords) +{ + int nwords = 0; + + while (nwords < maxwords && + (words[nwords] = nearly_strtok_r(&buf, " \t\r\n")) != NULL) + nwords++; + if (buf) + return -EINVAL; /* ran out of words[] before bytes */ + + if (verbose) { + int i; + printk(KERN_INFO "%s: split into words:", __func__); + for (i = 0 ; i < nwords ; i++) + printk(" \"%s\"", words[i]); + printk("\n"); + } + + return nwords; +} + +/* + * Parse a single line number. Note that the empty string "" + * is treated as a special case and converted to zero, which + * is later treated as a "don't care" value. + */ +static inline int parse_lineno(const char *str, unsigned int *val) +{ + char *end = NULL; + BUG_ON(str == NULL); + if (*str == '\0') { + *val = 0; + return 0; + } + *val = simple_strtoul(str, &end, 10); + return end == NULL || end == str || *end != '\0' ? -EINVAL : 0; +} + +/* + * Undo octal escaping in a string, inplace. This is useful to + * allow the user to express a query which matches a format + * containing embedded spaces. + */ +#define isodigit(c) ((c) >= '0' && (c) <= '7') +static char *unescape(char *str) +{ + char *in = str; + char *out = str; + + while (*in) { + if (*in == '\\') { + if (in[1] == '\\') { + *out++ = '\\'; + in += 2; + continue; + } else if (in[1] == 't') { + *out++ = '\t'; + in += 2; + continue; + } else if (in[1] == 'n') { + *out++ = '\n'; + in += 2; + continue; + } else if (isodigit(in[1]) && + isodigit(in[2]) && + isodigit(in[3])) { + *out++ = ((in[1] - '0')<<6) | + ((in[2] - '0')<<3) | + (in[3] - '0'); + in += 4; + continue; + } + } + *out++ = *in++; + } + *out = '\0'; + + return str; +} + +/* + * Parse words[] as a ddebug query specification, which is a series + * of (keyword, value) pairs chosen from these possibilities: + * + * func + * file + * file + * module + * format + * line + * line - // where either may be empty + */ +static int ddebug_parse_query(char *words[], int nwords, + struct ddebug_query *query) +{ + unsigned int i; + + /* check we have an even number of words */ + if (nwords % 2 != 0) + return -EINVAL; + memset(query, 0, sizeof(*query)); + + for (i = 0 ; i < nwords ; i += 2) { + if (!strcmp(words[i], "func")) + query->function = words[i+1]; + else if (!strcmp(words[i], "file")) + query->filename = words[i+1]; + else if (!strcmp(words[i], "module")) + query->module = words[i+1]; + else if (!strcmp(words[i], "format")) + query->format = unescape(words[i+1]); + else if (!strcmp(words[i], "line")) { + char *first = words[i+1]; + char *last = strchr(first, '-'); + if (last) + *last++ = '\0'; + if (parse_lineno(first, &query->first_lineno) < 0) + return -EINVAL; + if (last != NULL) { + /* range - */ + if (parse_lineno(last, &query->last_lineno) < 0) + return -EINVAL; + } else { + query->last_lineno = query->first_lineno; + } + } else { + if (verbose) + printk(KERN_ERR "%s: unknown keyword \"%s\"\n", + __func__, words[i]); + return -EINVAL; + } + } + + if (verbose) + printk(KERN_INFO "%s: q->function=\"%s\" q->filename=\"%s\" " + "q->module=\"%s\" q->format=\"%s\" q->lineno=%u-%u\n", + __func__, query->function, query->filename, + query->module, query->format, query->first_lineno, + query->last_lineno); + + return 0; +} + +/* + * Parse `str' as a flags specification, format [-+=][p]+. + * Sets up *maskp and *flagsp to be used when changing the + * flags fields of matched _ddebug's. Returns 0 on success + * or <0 on error. + */ +static int ddebug_parse_flags(const char *str, unsigned int *flagsp, + unsigned int *maskp) +{ + unsigned flags = 0; + int op = '='; + + switch (*str) { + case '+': + case '-': + case '=': + op = *str++; + break; + default: + return -EINVAL; + } + if (verbose) + printk(KERN_INFO "%s: op='%c'\n", __func__, op); + + for ( ; *str ; ++str) { + switch (*str) { + case 'p': + flags |= _DPRINTK_FLAGS_PRINT; + break; + default: + return -EINVAL; + } + } + if (flags == 0) + return -EINVAL; + if (verbose) + printk(KERN_INFO "%s: flags=0x%x\n", __func__, flags); + + /* calculate final *flagsp, *maskp according to mask and op */ + switch (op) { + case '=': + *maskp = 0; + *flagsp = flags; + break; + case '+': + *maskp = ~0U; + *flagsp = flags; + break; + case '-': + *maskp = ~flags; + *flagsp = 0; + break; + } + if (verbose) + printk(KERN_INFO "%s: *flagsp=0x%x *maskp=0x%x\n", + __func__, *flagsp, *maskp); + return 0; +} + +/* + * File_ops->write method for /dynamic_debug/conrol. Gathers the + * command text from userspace, parses and executes it. + */ +static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + unsigned int flags = 0, mask = 0; + struct ddebug_query query; +#define MAXWORDS 9 + int nwords; + char *words[MAXWORDS]; + char tmpbuf[256]; + + if (len == 0) + return 0; + /* we don't check *offp -- multiple writes() are allowed */ + if (len > sizeof(tmpbuf)-1) + return -E2BIG; + if (copy_from_user(tmpbuf, ubuf, len)) + return -EFAULT; + tmpbuf[len] = '\0'; + if (verbose) + printk(KERN_INFO "%s: read %d bytes from userspace\n", + __func__, (int)len); + + nwords = ddebug_tokenize(tmpbuf, words, MAXWORDS); + if (nwords < 0) + return -EINVAL; + if (ddebug_parse_query(words, nwords-1, &query)) + return -EINVAL; + if (ddebug_parse_flags(words[nwords-1], &flags, &mask)) + return -EINVAL; + + /* actually go and implement the change */ + ddebug_change(&query, flags, mask); + + *offp += len; + return len; +} + +/* + * Set the iterator to point to the first _ddebug object + * and return a pointer to that first object. Returns + * NULL if there are no _ddebugs at all. + */ +static struct _ddebug *ddebug_iter_first(struct ddebug_iter *iter) +{ + if (list_empty(&ddebug_tables)) { + iter->table = NULL; + iter->idx = 0; + return NULL; + } + iter->table = list_entry(ddebug_tables.next, + struct ddebug_table, link); + iter->idx = 0; + return &iter->table->ddebugs[iter->idx]; +} + +/* + * Advance the iterator to point to the next _ddebug + * object from the one the iterator currently points at, + * and returns a pointer to the new _ddebug. Returns + * NULL if the iterator has seen all the _ddebugs. + */ +static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter) +{ + if (iter->table == NULL) + return NULL; + if (++iter->idx == iter->table->num_ddebugs) { + /* iterate to next table */ + iter->idx = 0; + if (list_is_last(&iter->table->link, &ddebug_tables)) { + iter->table = NULL; + return NULL; + } + iter->table = list_entry(iter->table->link.next, + struct ddebug_table, link); + } + return &iter->table->ddebugs[iter->idx]; +} + +/* + * Seq_ops start method. Called at the start of every + * read() call from userspace. Takes the ddebug_lock and + * seeks the seq_file's iterator to the given position. + */ +static void *ddebug_proc_start(struct seq_file *m, loff_t *pos) +{ + struct ddebug_iter *iter = m->private; + struct _ddebug *dp; + int n = *pos; + + if (verbose) + printk(KERN_INFO "%s: called m=%p *pos=%lld\n", + __func__, m, (unsigned long long)*pos); + + mutex_lock(&ddebug_lock); + + if (!n) + return SEQ_START_TOKEN; + if (n < 0) + return NULL; + dp = ddebug_iter_first(iter); + while (dp != NULL && --n > 0) + dp = ddebug_iter_next(iter); + return dp; +} + +/* + * Seq_ops next method. Called several times within a read() + * call from userspace, with ddebug_lock held. Walks to the + * next _ddebug object with a special case for the header line. + */ +static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos) +{ + struct ddebug_iter *iter = m->private; + struct _ddebug *dp; + + if (verbose) + printk(KERN_INFO "%s: called m=%p p=%p *pos=%lld\n", + __func__, m, p, (unsigned long long)*pos); + + if (p == SEQ_START_TOKEN) + dp = ddebug_iter_first(iter); + else + dp = ddebug_iter_next(iter); + ++*pos; + return dp; +} + +/* + * Seq_ops show method. Called several times within a read() + * call from userspace, with ddebug_lock held. Formats the + * current _ddebug as a single human-readable line, with a + * special case for the header line. + */ +static int ddebug_proc_show(struct seq_file *m, void *p) +{ + struct ddebug_iter *iter = m->private; + struct _ddebug *dp = p; + char flagsbuf[8]; + + if (verbose) + printk(KERN_INFO "%s: called m=%p p=%p\n", + __func__, m, p); + + if (p == SEQ_START_TOKEN) { + seq_puts(m, + "# filename:lineno [module]function flags format\n"); + return 0; + } + + seq_printf(m, "%s:%u [%s]%s %s \"", + dp->filename, dp->lineno, + iter->table->mod_name, dp->function, + ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf))); + seq_escape(m, dp->format, "\t\r\n\""); + seq_puts(m, "\"\n"); + + return 0; +} + +/* + * Seq_ops stop method. Called at the end of each read() + * call from userspace. Drops ddebug_lock. + */ +static void ddebug_proc_stop(struct seq_file *m, void *p) +{ + if (verbose) + printk(KERN_INFO "%s: called m=%p p=%p\n", + __func__, m, p); + mutex_unlock(&ddebug_lock); +} + +static const struct seq_operations ddebug_proc_seqops = { + .start = ddebug_proc_start, + .next = ddebug_proc_next, + .show = ddebug_proc_show, + .stop = ddebug_proc_stop +}; + +/* + * File_ops->open method for /dynamic_debug/control. Does the seq_file + * setup dance, and also creates an iterator to walk the _ddebugs. + * Note that we create a seq_file always, even for O_WRONLY files + * where it's not needed, as doing so simplifies the ->release method. + */ +static int ddebug_proc_open(struct inode *inode, struct file *file) +{ + struct ddebug_iter *iter; + int err; + + if (verbose) + printk(KERN_INFO "%s: called\n", __func__); + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (iter == NULL) + return -ENOMEM; + + err = seq_open(file, &ddebug_proc_seqops); + if (err) { + kfree(iter); + return err; + } + ((struct seq_file *) file->private_data)->private = iter; + return 0; +} + +static const struct file_operations ddebug_proc_fops = { + .owner = THIS_MODULE, + .open = ddebug_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, + .write = ddebug_proc_write +}; + +/* + * Allocate a new ddebug_table for the given module + * and add it to the global list. + */ +int ddebug_add_module(struct _ddebug *tab, unsigned int n, + const char *name) +{ + struct ddebug_table *dt; + char *new_name; + + dt = kzalloc(sizeof(*dt), GFP_KERNEL); + if (dt == NULL) + return -ENOMEM; + new_name = kstrdup(name, GFP_KERNEL); + if (new_name == NULL) { + kfree(dt); + return -ENOMEM; + } + dt->mod_name = new_name; + dt->num_ddebugs = n; + dt->num_enabled = 0; + dt->ddebugs = tab; + + mutex_lock(&ddebug_lock); + list_add_tail(&dt->link, &ddebug_tables); + mutex_unlock(&ddebug_lock); + + if (verbose) + printk(KERN_INFO "%u debug prints in module %s\n", + n, dt->mod_name); + return 0; +} +EXPORT_SYMBOL_GPL(ddebug_add_module); + +static void ddebug_table_free(struct ddebug_table *dt) +{ + list_del_init(&dt->link); + kfree(dt->mod_name); + kfree(dt); +} + +/* + * Called in response to a module being unloaded. Removes + * any ddebug_table's which point at the module. + */ +int ddebug_remove_module(char *mod_name) +{ + struct ddebug_table *dt, *nextdt; + int ret = -ENOENT; + + if (verbose) + printk(KERN_INFO "%s: removing module \"%s\"\n", + __func__, mod_name); + + mutex_lock(&ddebug_lock); + list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) { + if (!strcmp(dt->mod_name, mod_name)) { + ddebug_table_free(dt); + ret = 0; + } + } + mutex_unlock(&ddebug_lock); + return ret; +} +EXPORT_SYMBOL_GPL(ddebug_remove_module); + +static void ddebug_remove_all_tables(void) +{ + mutex_lock(&ddebug_lock); + while (!list_empty(&ddebug_tables)) { + struct ddebug_table *dt = list_entry(ddebug_tables.next, + struct ddebug_table, + link); + ddebug_table_free(dt); + } + mutex_unlock(&ddebug_lock); +} + +static int __init dynamic_debug_init(void) +{ + struct dentry *dir, *file; + struct _ddebug *iter, *iter_start; + const char *modname = NULL; + int ret = 0; + int n = 0; + + dir = debugfs_create_dir("dynamic_debug", NULL); + if (!dir) + return -ENOMEM; + file = debugfs_create_file("control", 0644, dir, NULL, + &ddebug_proc_fops); + if (!file) { + debugfs_remove(dir); + return -ENOMEM; + } + if (__start___verbose != __stop___verbose) { + iter = __start___verbose; + modname = iter->modname; + iter_start = iter; + for (; iter < __stop___verbose; iter++) { + if (strcmp(modname, iter->modname)) { + ret = ddebug_add_module(iter_start, n, modname); + if (ret) + goto out_free; + n = 0; + modname = iter->modname; + iter_start = iter; + } + n++; + } + ret = ddebug_add_module(iter_start, n, modname); + } +out_free: + if (ret) { + ddebug_remove_all_tables(); + debugfs_remove(dir); + debugfs_remove(file); + } + return 0; +} +module_init(dynamic_debug_init); diff --git a/lib/dynamic_printk.c b/lib/dynamic_printk.c deleted file mode 100644 index 165a19763dc9..000000000000 --- a/lib/dynamic_printk.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * lib/dynamic_printk.c - * - * make pr_debug()/dev_dbg() calls runtime configurable based upon their - * their source module. - * - * Copyright (C) 2008 Red Hat, Inc., Jason Baron - */ - -#include -#include -#include -#include -#include -#include - -extern struct mod_debug __start___verbose[]; -extern struct mod_debug __stop___verbose[]; - -struct debug_name { - struct hlist_node hlist; - struct hlist_node hlist2; - int hash1; - int hash2; - char *name; - int enable; - int type; -}; - -static int nr_entries; -static int num_enabled; -int dynamic_enabled = DYNAMIC_ENABLED_NONE; -static struct hlist_head module_table[DEBUG_HASH_TABLE_SIZE] = - { [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT }; -static struct hlist_head module_table2[DEBUG_HASH_TABLE_SIZE] = - { [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT }; -static DECLARE_MUTEX(debug_list_mutex); - -/* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which - * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They - * use independent hash functions, to reduce the chance of false positives. - */ -long long dynamic_printk_enabled; -EXPORT_SYMBOL_GPL(dynamic_printk_enabled); -long long dynamic_printk_enabled2; -EXPORT_SYMBOL_GPL(dynamic_printk_enabled2); - -/* returns the debug module pointer. */ -static struct debug_name *find_debug_module(char *module_name) -{ - int i; - struct hlist_head *head; - struct hlist_node *node; - struct debug_name *element; - - element = NULL; - for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) { - head = &module_table[i]; - hlist_for_each_entry_rcu(element, node, head, hlist) - if (!strcmp(element->name, module_name)) - return element; - } - return NULL; -} - -/* returns the debug module pointer. */ -static struct debug_name *find_debug_module_hash(char *module_name, int hash) -{ - struct hlist_head *head; - struct hlist_node *node; - struct debug_name *element; - - element = NULL; - head = &module_table[hash]; - hlist_for_each_entry_rcu(element, node, head, hlist) - if (!strcmp(element->name, module_name)) - return element; - return NULL; -} - -/* caller must hold mutex*/ -static int __add_debug_module(char *mod_name, int hash, int hash2) -{ - struct debug_name *new; - char *module_name; - int ret = 0; - - if (find_debug_module(mod_name)) { - ret = -EINVAL; - goto out; - } - module_name = kmalloc(strlen(mod_name) + 1, GFP_KERNEL); - if (!module_name) { - ret = -ENOMEM; - goto out; - } - module_name = strcpy(module_name, mod_name); - module_name[strlen(mod_name)] = '\0'; - new = kzalloc(sizeof(struct debug_name), GFP_KERNEL); - if (!new) { - kfree(module_name); - ret = -ENOMEM; - goto out; - } - INIT_HLIST_NODE(&new->hlist); - INIT_HLIST_NODE(&new->hlist2); - new->name = module_name; - new->hash1 = hash; - new->hash2 = hash2; - hlist_add_head_rcu(&new->hlist, &module_table[hash]); - hlist_add_head_rcu(&new->hlist2, &module_table2[hash2]); - nr_entries++; -out: - return ret; -} - -int unregister_dynamic_debug_module(char *mod_name) -{ - struct debug_name *element; - int ret = 0; - - down(&debug_list_mutex); - element = find_debug_module(mod_name); - if (!element) { - ret = -EINVAL; - goto out; - } - hlist_del_rcu(&element->hlist); - hlist_del_rcu(&element->hlist2); - synchronize_rcu(); - kfree(element->name); - if (element->enable) - num_enabled--; - kfree(element); - nr_entries--; -out: - up(&debug_list_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(unregister_dynamic_debug_module); - -int register_dynamic_debug_module(char *mod_name, int type, char *share_name, - char *flags, int hash, int hash2) -{ - struct debug_name *elem; - int ret = 0; - - down(&debug_list_mutex); - elem = find_debug_module(mod_name); - if (!elem) { - if (__add_debug_module(mod_name, hash, hash2)) - goto out; - elem = find_debug_module(mod_name); - if (dynamic_enabled == DYNAMIC_ENABLED_ALL && - !strcmp(mod_name, share_name)) { - elem->enable = true; - num_enabled++; - } - } - elem->type |= type; -out: - up(&debug_list_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(register_dynamic_debug_module); - -int __dynamic_dbg_enabled_helper(char *mod_name, int type, int value, int hash) -{ - struct debug_name *elem; - int ret = 0; - - if (dynamic_enabled == DYNAMIC_ENABLED_ALL) - return 1; - rcu_read_lock(); - elem = find_debug_module_hash(mod_name, hash); - if (elem && elem->enable) - ret = 1; - rcu_read_unlock(); - return ret; -} -EXPORT_SYMBOL_GPL(__dynamic_dbg_enabled_helper); - -static void set_all(bool enable) -{ - struct debug_name *e; - struct hlist_node *node; - int i; - long long enable_mask; - - for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) { - if (module_table[i].first != NULL) { - hlist_for_each_entry(e, node, &module_table[i], hlist) { - e->enable = enable; - } - } - } - if (enable) - enable_mask = ULLONG_MAX; - else - enable_mask = 0; - dynamic_printk_enabled = enable_mask; - dynamic_printk_enabled2 = enable_mask; -} - -static int disabled_hash(int i, bool first_table) -{ - struct debug_name *e; - struct hlist_node *node; - - if (first_table) { - hlist_for_each_entry(e, node, &module_table[i], hlist) { - if (e->enable) - return 0; - } - } else { - hlist_for_each_entry(e, node, &module_table2[i], hlist2) { - if (e->enable) - return 0; - } - } - return 1; -} - -static ssize_t pr_debug_write(struct file *file, const char __user *buf, - size_t length, loff_t *ppos) -{ - char *buffer, *s, *value_str, *setting_str; - int err, value; - struct debug_name *elem = NULL; - int all = 0; - - if (length > PAGE_SIZE || length < 0) - return -EINVAL; - - buffer = (char *)__get_free_page(GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - err = -EFAULT; - if (copy_from_user(buffer, buf, length)) - goto out; - - err = -EINVAL; - if (length < PAGE_SIZE) - buffer[length] = '\0'; - else if (buffer[PAGE_SIZE-1]) - goto out; - - err = -EINVAL; - down(&debug_list_mutex); - - if (strncmp("set", buffer, 3)) - goto out_up; - s = buffer + 3; - setting_str = strsep(&s, "="); - if (s == NULL) - goto out_up; - setting_str = strstrip(setting_str); - value_str = strsep(&s, " "); - if (s == NULL) - goto out_up; - s = strstrip(s); - if (!strncmp(s, "all", 3)) - all = 1; - else - elem = find_debug_module(s); - if (!strncmp(setting_str, "enable", 6)) { - value = !!simple_strtol(value_str, NULL, 10); - if (all) { - if (value) { - set_all(true); - num_enabled = nr_entries; - dynamic_enabled = DYNAMIC_ENABLED_ALL; - } else { - set_all(false); - num_enabled = 0; - dynamic_enabled = DYNAMIC_ENABLED_NONE; - } - err = 0; - } else if (elem) { - if (value && (elem->enable == 0)) { - dynamic_printk_enabled |= (1LL << elem->hash1); - dynamic_printk_enabled2 |= (1LL << elem->hash2); - elem->enable = 1; - num_enabled++; - dynamic_enabled = DYNAMIC_ENABLED_SOME; - err = 0; - printk(KERN_DEBUG - "debugging enabled for module %s\n", - elem->name); - } else if (!value && (elem->enable == 1)) { - elem->enable = 0; - num_enabled--; - if (disabled_hash(elem->hash1, true)) - dynamic_printk_enabled &= - ~(1LL << elem->hash1); - if (disabled_hash(elem->hash2, false)) - dynamic_printk_enabled2 &= - ~(1LL << elem->hash2); - if (num_enabled) - dynamic_enabled = DYNAMIC_ENABLED_SOME; - else - dynamic_enabled = DYNAMIC_ENABLED_NONE; - err = 0; - printk(KERN_DEBUG - "debugging disabled for module %s\n", - elem->name); - } - } - } - if (!err) - err = length; -out_up: - up(&debug_list_mutex); -out: - free_page((unsigned long)buffer); - return err; -} - -static void *pr_debug_seq_start(struct seq_file *f, loff_t *pos) -{ - return (*pos < DEBUG_HASH_TABLE_SIZE) ? pos : NULL; -} - -static void *pr_debug_seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - (*pos)++; - if (*pos >= DEBUG_HASH_TABLE_SIZE) - return NULL; - return pos; -} - -static void pr_debug_seq_stop(struct seq_file *s, void *v) -{ - /* Nothing to do */ -} - -static int pr_debug_seq_show(struct seq_file *s, void *v) -{ - struct hlist_head *head; - struct hlist_node *node; - struct debug_name *elem; - unsigned int i = *(loff_t *) v; - - rcu_read_lock(); - head = &module_table[i]; - hlist_for_each_entry_rcu(elem, node, head, hlist) { - seq_printf(s, "%s enabled=%d", elem->name, elem->enable); - seq_printf(s, "\n"); - } - rcu_read_unlock(); - return 0; -} - -static struct seq_operations pr_debug_seq_ops = { - .start = pr_debug_seq_start, - .next = pr_debug_seq_next, - .stop = pr_debug_seq_stop, - .show = pr_debug_seq_show -}; - -static int pr_debug_open(struct inode *inode, struct file *filp) -{ - return seq_open(filp, &pr_debug_seq_ops); -} - -static const struct file_operations pr_debug_operations = { - .open = pr_debug_open, - .read = seq_read, - .write = pr_debug_write, - .llseek = seq_lseek, - .release = seq_release, -}; - -static int __init dynamic_printk_init(void) -{ - struct dentry *dir, *file; - struct mod_debug *iter; - unsigned long value; - - dir = debugfs_create_dir("dynamic_printk", NULL); - if (!dir) - return -ENOMEM; - file = debugfs_create_file("modules", 0644, dir, NULL, - &pr_debug_operations); - if (!file) { - debugfs_remove(dir); - return -ENOMEM; - } - for (value = (unsigned long)__start___verbose; - value < (unsigned long)__stop___verbose; - value += sizeof(struct mod_debug)) { - iter = (struct mod_debug *)value; - register_dynamic_debug_module(iter->modname, - iter->type, - iter->logical_modname, - iter->flag_names, iter->hash, iter->hash2); - } - if (dynamic_enabled == DYNAMIC_ENABLED_ALL) - set_all(true); - return 0; -} -module_init(dynamic_printk_init); -/* may want to move this earlier so we can get traces as early as possible */ - -static int __init dynamic_printk_setup(char *str) -{ - if (str) - return -ENOENT; - dynamic_enabled = DYNAMIC_ENABLED_ALL; - return 0; -} -/* Use early_param(), so we can get debug output as early as possible */ -early_param("dynamic_printk", dynamic_printk_setup); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 9e169ef2e854..12bd09dbd36c 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -66,7 +66,7 @@ void struct nf_conntrack_expect *exp) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn); -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_PRINTK_DEBUG) +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) /* PptpControlMessageType names */ const char *const pptp_msg_name[] = { "UNKNOWN_MESSAGE", diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index e06365775bdf..c18fa150b6fe 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -97,7 +97,7 @@ modname_flags = $(if $(filter 1,$(words $(modname))),\ -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))") #hash values -ifdef CONFIG_DYNAMIC_PRINTK_DEBUG +ifdef CONFIG_DYNAMIC_DEBUG debug_flags = -D"DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname))"\ -D"DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname))" else -- cgit v1.2.3-59-g8ed1b From 86151fdf38b3795f292b39defbff39d2684b9c8c Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Thu, 5 Feb 2009 11:53:15 -0500 Subject: dynamic debug: update docs updates the documentation for 'dynamic debug' feature. Signed-off-by: Greg Banks Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- Documentation/dynamic-debug-howto.txt | 232 ++++++++++++++++++++++++++++++++++ lib/Kconfig.debug | 72 ++++++----- 2 files changed, 273 insertions(+), 31 deletions(-) create mode 100644 Documentation/dynamic-debug-howto.txt (limited to 'Documentation') diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt new file mode 100644 index 000000000000..68394825e86b --- /dev/null +++ b/Documentation/dynamic-debug-howto.txt @@ -0,0 +1,232 @@ + +Introduction +============ + +This document describes how to use the dynamic debug (ddebug) feature. + +Dynamic debug is designed to allow you to dynamically enable/disable kernel +code to obtain additional kernel information. Currently, if +CONFIG_DYNAMIC_DEBUG is set, then all pr_debug()/dev_debug() calls can be +dynamically enabled per-callsite. + +Dynamic debug has even more useful features: + + * Simple query language allows turning on and off debugging statements by + matching any combination of: + + - source filename + - function name + - line number (including ranges of line numbers) + - module name + - format string + + * Provides a debugfs control file: /dynamic_debug/control which can be + read to display the complete list of known debug statements, to help guide you + +Controlling dynamic debug Behaviour +=============================== + +The behaviour of pr_debug()/dev_debug()s are controlled via writing to a +control file in the 'debugfs' filesystem. Thus, you must first mount the debugfs +filesystem, in order to make use of this feature. Subsequently, we refer to the +control file as: /dynamic_debug/control. For example, if you want to +enable printing from source file 'svcsock.c', line 1603 you simply do: + +nullarbor:~ # echo 'file svcsock.c line 1603 +p' > + /dynamic_debug/control + +If you make a mistake with the syntax, the write will fail thus: + +nullarbor:~ # echo 'file svcsock.c wtf 1 +p' > + /dynamic_debug/control +-bash: echo: write error: Invalid argument + +Viewing Dynamic Debug Behaviour +=========================== + +You can view the currently configured behaviour of all the debug statements +via: + +nullarbor:~ # cat /dynamic_debug/control +# filename:lineno [module]function flags format +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup - "SVCRDMA\040Module\040Removed,\040deregister\040RPC\040RDMA\040transport\012" +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init - "\011max_inline\040\040\040\040\040\040\040:\040%d\012" +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init - "\011sq_depth\040\040\040\040\040\040\040\040\040:\040%d\012" +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init - "\011max_requests\040\040\040\040\040:\040%d\012" +... + + +You can also apply standard Unix text manipulation filters to this +data, e.g. + +nullarbor:~ # grep -i rdma /dynamic_debug/control | wc -l +62 + +nullarbor:~ # grep -i tcp /dynamic_debug/control | wc -l +42 + +Note in particular that the third column shows the enabled behaviour +flags for each debug statement callsite (see below for definitions of the +flags). The default value, no extra behaviour enabled, is "-". So +you can view all the debug statement callsites with any non-default flags: + +nullarbor:~ # awk '$3 != "-"' /dynamic_debug/control +# filename:lineno [module]function flags format +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p "svc_process:\040st_sendto\040returned\040%d\012" + + +Command Language Reference +========================== + +At the lexical level, a command comprises a sequence of words separated +by whitespace characters. Note that newlines are treated as word +separators and do *not* end a command or allow multiple commands to +be done together. So these are all equivalent: + +nullarbor:~ # echo -c 'file svcsock.c line 1603 +p' > + /dynamic_debug/control +nullarbor:~ # echo -c ' file svcsock.c line 1603 +p ' > + /dynamic_debug/control +nullarbor:~ # echo -c 'file svcsock.c\nline 1603 +p' > + /dynamic_debug/control +nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > + /dynamic_debug/control + +Commands are bounded by a write() system call. If you want to do +multiple commands you need to do a separate "echo" for each, like: + +nullarbor:~ # echo 'file svcsock.c line 1603 +p' > /proc/dprintk ;\ +> echo 'file svcsock.c line 1563 +p' > /proc/dprintk + +or even like: + +nullarbor:~ # ( +> echo 'file svcsock.c line 1603 +p' ;\ +> echo 'file svcsock.c line 1563 +p' ;\ +> ) > /proc/dprintk + +At the syntactical level, a command comprises a sequence of match +specifications, followed by a flags change specification. + +command ::= match-spec* flags-spec + +The match-spec's are used to choose a subset of the known dprintk() +callsites to which to apply the flags-spec. Think of them as a query +with implicit ANDs between each pair. Note that an empty list of +match-specs is possible, but is not very useful because it will not +match any debug statement callsites. + +A match specification comprises a keyword, which controls the attribute +of the callsite to be compared, and a value to compare against. Possible +keywords are: + +match-spec ::= 'func' string | + 'file' string | + 'module' string | + 'format' string | + 'line' line-range + +line-range ::= lineno | + '-'lineno | + lineno'-' | + lineno'-'lineno +// Note: line-range cannot contain space, e.g. +// "1-30" is valid range but "1 - 30" is not. + +lineno ::= unsigned-int + +The meanings of each keyword are: + +func + The given string is compared against the function name + of each callsite. Example: + + func svc_tcp_accept + +file + The given string is compared against either the full + pathname or the basename of the source file of each + callsite. Examples: + + file svcsock.c + file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c + +module + The given string is compared against the module name + of each callsite. The module name is the string as + seen in "lsmod", i.e. without the directory or the .ko + suffix and with '-' changed to '_'. Examples: + + module sunrpc + module nfsd + +format + The given string is searched for in the dynamic debug format + string. Note that the string does not need to match the + entire format, only some part. Whitespace and other + special characters can be escaped using C octal character + escape \ooo notation, e.g. the space character is \040. + Examples: + + format svcrdma: // many of the NFS/RDMA server dprintks + format readahead // some dprintks in the readahead cache + format nfsd:\040SETATTR // how to match a format with whitespace + +line + The given line number or range of line numbers is compared + against the line number of each dprintk() callsite. A single + line number matches the callsite line number exactly. A + range of line numbers matches any callsite between the first + and last line number inclusive. An empty first number means + the first line in the file, an empty line number means the + last number in the file. Examples: + + line 1603 // exactly line 1603 + line 1600-1605 // the six lines from line 1600 to line 1605 + line -1605 // the 1605 lines from line 1 to line 1605 + line 1600- // all lines from line 1600 to the end of the file + +The flags specification comprises a change operation followed +by one or more flag characters. The change operation is one +of the characters: + +- + remove the given flags + ++ + add the given flags + += + set the flags to the given flags + +The flags are: + +p + Causes a printk() message to be emitted to dmesg + +Note the regexp ^[-+=][scp]+$ matches a flags specification. +Note also that there is no convenient syntax to remove all +the flags at once, you need to use "-psc". + +Examples +======== + +// enable the message at line 1603 of file svcsock.c +nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > + /dynamic_debug/control + +// enable all the messages in file svcsock.c +nullarbor:~ # echo -n 'file svcsock.c +p' > + /dynamic_debug/control + +// enable all the messages in the NFS server module +nullarbor:~ # echo -n 'module nfsd +p' > + /dynamic_debug/control + +// enable all 12 messages in the function svc_process() +nullarbor:~ # echo -n 'func svc_process +p' > + /dynamic_debug/control + +// disable all 12 messages in the function svc_process() +nullarbor:~ # echo -n 'func svc_process -p' > + /dynamic_debug/control diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 0dd1c04c7323..8fee0a13ac58 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -848,59 +848,69 @@ config BUILD_DOCSRC Say N if you are unsure. config DYNAMIC_DEBUG - bool "Enable dynamic printk() call support" + bool "Enable dynamic printk() support" default n depends on PRINTK + depends on DEBUG_FS select PRINTK_DEBUG help Compiles debug level messages into the kernel, which would not otherwise be available at runtime. These messages can then be - enabled/disabled on a per module basis. This mechanism implicitly - enables all pr_debug() and dev_dbg() calls. The impact of this - compile option is a larger kernel text size of about 2%. + enabled/disabled based on various levels of scope - per source file, + function, module, format string, and line number. This mechanism + implicitly enables all pr_debug() and dev_dbg() calls. The impact of + this compile option is a larger kernel text size of about 2%. Usage: - Dynamic debugging is controlled by the debugfs file, - dynamic_printk/modules. This file contains a list of the modules that - can be enabled. The format of the file is the module name, followed - by a set of flags that can be enabled. The first flag is always the - 'enabled' flag. For example: + Dynamic debugging is controlled via the 'dynamic_debug/ddebug' file, + which is contained in the 'debugfs' filesystem. Thus, the debugfs + filesystem must first be mounted before making use of this feature. + We refer the control file as: /dynamic_debug/ddebug. This + file contains a list of the debug statements that can be enabled. The + format for each line of the file is: - - . - . - . + filename:lineno [module]function flags format - : Name of the module in which the debug call resides - : whether the messages are enabled or not + filename : source file of the debug statement + lineno : line number of the debug statement + module : module that contains the debug statement + function : function that contains the debug statement + flags : 'p' means the line is turned 'on' for printing + format : the format used for the debug statement From a live system: - snd_hda_intel enabled=0 - fixup enabled=0 - driver enabled=0 + nullarbor:~ # cat /dynamic_debug/ddebug + # filename:lineno [module]function flags format + fs/aio.c:222 [aio]__put_ioctx - "__put_ioctx:\040freeing\040%p\012" + fs/aio.c:248 [aio]ioctx_alloc - "ENOMEM:\040nr_events\040too\040high\012" + fs/aio.c:1770 [aio]sys_io_cancel - "calling\040cancel\012" - Enable a module: + Example usage: - $echo "set enabled=1 " > dynamic_printk/modules + // enable the message at line 1603 of file svcsock.c + nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > + /dynamic_debug/ddebug - Disable a module: + // enable all the messages in file svcsock.c + nullarbor:~ # echo -n 'file svcsock.c +p' > + /dynamic_debug/ddebug - $echo "set enabled=0 " > dynamic_printk/modules + // enable all the messages in the NFS server module + nullarbor:~ # echo -n 'module nfsd +p' > + /dynamic_debug/ddebug - Enable all modules: + // enable all 12 messages in the function svc_process() + nullarbor:~ # echo -n 'func svc_process +p' > + /dynamic_debug/ddebug - $echo "set enabled=1 all" > dynamic_printk/modules + // disable all 12 messages in the function svc_process() + nullarbor:~ # echo -n 'func svc_process -p' > + /dynamic_debug/ddebug - Disable all modules: - - $echo "set enabled=0 all" > dynamic_printk/modules - - Finally, passing "dynamic_printk" at the command line enables - debugging for all modules. This mode can be turned off via the above - disable command. + See Documentation/dynamic-debug-howto.txt for additional information. source "samples/Kconfig" -- cgit v1.2.3-59-g8ed1b From 9898abb3d23311fa227a7f46bf4e40fd2954057f Mon Sep 17 00:00:00 2001 From: Greg Banks Date: Fri, 6 Feb 2009 12:54:26 +1100 Subject: Dynamic debug: allow simple quoting of words Allow simple quoting of words in the dynamic debug control language. This allows more natural specification when using the control language to match against printk formats, e.g #echo -n 'format "Setting node for non-present cpu" +p' > /mnt/debugfs/dynamic_debug/control instead of #echo -n 'format Setting\040node\040for\040non-present\040cpu +p' > /mnt/debugfs/dynamic_debug/control Adjust the dynamic debug documention to describe that and provide a new example. Adjust the existing examples in the documentation to reflect the current whitespace escaping behaviour when reading the control file. Fix some minor documentation trailing whitespace. Signed-off-by: Greg Banks Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- Documentation/dynamic-debug-howto.txt | 20 +++++++++---- lib/dynamic_debug.c | 53 ++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 26 deletions(-) (limited to 'Documentation') diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt index 68394825e86b..674c5663d346 100644 --- a/Documentation/dynamic-debug-howto.txt +++ b/Documentation/dynamic-debug-howto.txt @@ -49,10 +49,10 @@ via: nullarbor:~ # cat /dynamic_debug/control # filename:lineno [module]function flags format -/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup - "SVCRDMA\040Module\040Removed,\040deregister\040RPC\040RDMA\040transport\012" -/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init - "\011max_inline\040\040\040\040\040\040\040:\040%d\012" -/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init - "\011sq_depth\040\040\040\040\040\040\040\040\040:\040%d\012" -/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init - "\011max_requests\040\040\040\040\040:\040%d\012" +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup - "SVCRDMA Module Removed, deregister RPC RDMA transport\012" +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init - "\011max_inline : %d\012" +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init - "\011sq_depth : %d\012" +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init - "\011max_requests : %d\012" ... @@ -72,7 +72,7 @@ you can view all the debug statement callsites with any non-default flags: nullarbor:~ # awk '$3 != "-"' /dynamic_debug/control # filename:lineno [module]function flags format -/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p "svc_process:\040st_sendto\040returned\040%d\012" +/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p "svc_process: st_sendto returned %d\012" Command Language Reference @@ -166,11 +166,15 @@ format entire format, only some part. Whitespace and other special characters can be escaped using C octal character escape \ooo notation, e.g. the space character is \040. + Alternatively, the string can be enclosed in double quote + characters (") or single quote characters ('). Examples: format svcrdma: // many of the NFS/RDMA server dprintks format readahead // some dprintks in the readahead cache - format nfsd:\040SETATTR // how to match a format with whitespace + format nfsd:\040SETATTR // one way to match a format with whitespace + format "nfsd: SETATTR" // a neater way to match a format with whitespace + format 'nfsd: SETATTR' // yet another way to match a format with whitespace line The given line number or range of line numbers is compared @@ -230,3 +234,7 @@ nullarbor:~ # echo -n 'func svc_process +p' > // disable all 12 messages in the function svc_process() nullarbor:~ # echo -n 'func svc_process -p' > /dynamic_debug/control + +// enable messages for NFS calls READ, READLINK, READDIR and READDIR+. +nullarbor:~ # echo -n 'format "nfsd: READ" +p' > + /dynamic_debug/control diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 9e123ae326bc..833139ce1e22 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -195,33 +195,46 @@ static void ddebug_change(const struct ddebug_query *query, printk(KERN_INFO "ddebug: no matches for query\n"); } -/* - * Wrapper around strsep() to collapse the multiple empty tokens - * that it returns when fed sequences of separator characters. - * Now, if we had strtok_r()... - */ -static inline char *nearly_strtok_r(char **p, const char *sep) -{ - char *r; - - while ((r = strsep(p, sep)) != NULL && *r == '\0') - ; - return r; -} - /* * Split the buffer `buf' into space-separated words. - * Return the number of such words or <0 on error. + * Handles simple " and ' quoting, i.e. without nested, + * embedded or escaped \". Return the number of words + * or <0 on error. */ static int ddebug_tokenize(char *buf, char *words[], int maxwords) { int nwords = 0; - while (nwords < maxwords && - (words[nwords] = nearly_strtok_r(&buf, " \t\r\n")) != NULL) - nwords++; - if (buf) - return -EINVAL; /* ran out of words[] before bytes */ + while (*buf) { + char *end; + + /* Skip leading whitespace */ + while (*buf && isspace(*buf)) + buf++; + if (!*buf) + break; /* oh, it was trailing whitespace */ + + /* Run `end' over a word, either whitespace separated or quoted */ + if (*buf == '"' || *buf == '\'') { + int quote = *buf++; + for (end = buf ; *end && *end != quote ; end++) + ; + if (!*end) + return -EINVAL; /* unclosed quote */ + } else { + for (end = buf ; *end && !isspace(*end) ; end++) + ; + BUG_ON(end == buf); + } + /* Here `buf' is the start of the word, `end' is one past the end */ + + if (nwords == maxwords) + return -EINVAL; /* ran out of words[] before bytes */ + if (*end) + *end++ = '\0'; /* terminate the word */ + words[nwords++] = buf; + buf = end; + } if (verbose) { int i; -- cgit v1.2.3-59-g8ed1b From 07e86f405addc6436eb969b8279bb14a6dcacce4 Mon Sep 17 00:00:00 2001 From: Avishay Traeger Date: Tue, 24 Mar 2009 12:40:18 +0100 Subject: block: Repeated lines in switching-sched.txt These lines appear in this file twice - removed one occurrence. Signed-off-by: Avishay Traeger Signed-off-by: Jens Axboe --- Documentation/block/switching-sched.txt | 6 ------ 1 file changed, 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/block/switching-sched.txt b/Documentation/block/switching-sched.txt index 634c952e1964..d5af3f630814 100644 --- a/Documentation/block/switching-sched.txt +++ b/Documentation/block/switching-sched.txt @@ -35,9 +35,3 @@ noop anticipatory deadline [cfq] # echo anticipatory > /sys/block/hda/queue/scheduler # cat /sys/block/hda/queue/scheduler noop [anticipatory] deadline cfq - -Each io queue has a set of io scheduler tunables associated with it. These -tunables control how the io scheduler works. You can find these entries -in: - -/sys/block//queue/iosched -- cgit v1.2.3-59-g8ed1b From 431429ff788598a19c1a193b9fca3961b7f55916 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Thu, 26 Mar 2009 15:23:55 +0100 Subject: [S390] hvc_iucv: Provide IUCV z/VM user ID filtering This patch introduces the kernel parameter hvc_iucv_allow= that specifies a comma-separated list of z/VM user IDs. If specified, the z/VM IUCV hypervisor console device driver accepts IUCV connections from listed z/VM user IDs only. Signed-off-by: Hendrik Brueckner Signed-off-by: Martin Schwidefsky --- Documentation/kernel-parameters.txt | 3 + drivers/char/hvc_iucv.c | 254 ++++++++++++++++++++++++++++++++++-- 2 files changed, 249 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 54f21a5c262b..9f932a76a940 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -829,6 +829,9 @@ and is between 256 and 4096 characters. It is defined in the file hvc_iucv= [S390] Number of z/VM IUCV hypervisor console (HVC) terminal devices. Valid values: 0..8 + hvc_iucv_allow= [S390] Comma-separated list of z/VM user IDs. + If specified, z/VM IUCV HVC accepts connections + from listed z/VM user IDs only. i8042.debug [HW] Toggle i8042 debug mode i8042.direct [HW] Put keyboard port into non-translated mode diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 146be5a60947..54481a887769 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -13,10 +13,11 @@ #include #include +#include #include #include #include -#include +#include #include #include #include @@ -95,6 +96,12 @@ static unsigned long hvc_iucv_devices = 1; /* Array of allocated hvc iucv tty lines... */ static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; #define IUCV_HVC_CON_IDX (0) +/* List of z/VM user ID filter entries (struct iucv_vmid_filter) */ +#define MAX_VMID_FILTER (500) +static size_t hvc_iucv_filter_size; +static void *hvc_iucv_filter; +static const char *hvc_iucv_filter_string; +static DEFINE_RWLOCK(hvc_iucv_filter_lock); /* Kmem cache and mempool for iucv_tty_buffer elements */ static struct kmem_cache *hvc_iucv_buffer_cache; @@ -617,6 +624,27 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) } } +/** + * hvc_iucv_filter_connreq() - Filter connection request based on z/VM user ID + * @ipvmid: Originating z/VM user ID (right padded with blanks) + * + * Returns 0 if the z/VM user ID @ipvmid is allowed to connection, otherwise + * non-zero. + */ +static int hvc_iucv_filter_connreq(u8 ipvmid[8]) +{ + size_t i; + + /* Note: default policy is ACCEPT if no filter is set */ + if (!hvc_iucv_filter_size) + return 0; + + for (i = 0; i < hvc_iucv_filter_size; i++) + if (0 == memcmp(ipvmid, hvc_iucv_filter + (8 * i), 8)) + return 0; + return 1; +} + /** * hvc_iucv_path_pending() - IUCV handler to process a connection request. * @path: Pending path (struct iucv_path) @@ -641,6 +669,7 @@ static int hvc_iucv_path_pending(struct iucv_path *path, { struct hvc_iucv_private *priv; u8 nuser_data[16]; + u8 vm_user_id[9]; int i, rc; priv = NULL; @@ -653,6 +682,20 @@ static int hvc_iucv_path_pending(struct iucv_path *path, if (!priv) return -ENODEV; + /* Enforce that ipvmid is allowed to connect to us */ + read_lock(&hvc_iucv_filter_lock); + rc = hvc_iucv_filter_connreq(ipvmid); + read_unlock(&hvc_iucv_filter_lock); + if (rc) { + iucv_path_sever(path, ipuser); + iucv_path_free(path); + memcpy(vm_user_id, ipvmid, 8); + vm_user_id[8] = 0; + pr_info("A connection request from z/VM user ID %s " + "was refused\n", vm_user_id); + return 0; + } + spin_lock(&priv->lock); /* If the terminal is already connected or being severed, then sever @@ -876,6 +919,171 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) return 0; } +/** + * hvc_iucv_parse_filter() - Parse filter for a single z/VM user ID + * @filter: String containing a comma-separated list of z/VM user IDs + */ +static const char *hvc_iucv_parse_filter(const char *filter, char *dest) +{ + const char *nextdelim, *residual; + size_t len; + + nextdelim = strchr(filter, ','); + if (nextdelim) { + len = nextdelim - filter; + residual = nextdelim + 1; + } else { + len = strlen(filter); + residual = filter + len; + } + + if (len == 0) + return ERR_PTR(-EINVAL); + + /* check for '\n' (if called from sysfs) */ + if (filter[len - 1] == '\n') + len--; + + if (len > 8) + return ERR_PTR(-EINVAL); + + /* pad with blanks and save upper case version of user ID */ + memset(dest, ' ', 8); + while (len--) + dest[len] = toupper(filter[len]); + return residual; +} + +/** + * hvc_iucv_setup_filter() - Set up z/VM user ID filter + * @filter: String consisting of a comma-separated list of z/VM user IDs + * + * The function parses the @filter string and creates an array containing + * the list of z/VM user ID filter entries. + * Return code 0 means success, -EINVAL if the filter is syntactically + * incorrect, -ENOMEM if there was not enough memory to allocate the + * filter list array, or -ENOSPC if too many z/VM user IDs have been specified. + */ +static int hvc_iucv_setup_filter(const char *val) +{ + const char *residual; + int err; + size_t size, count; + void *array, *old_filter; + + count = strlen(val); + if (count == 0 || (count == 1 && val[0] == '\n')) { + size = 0; + array = NULL; + goto out_replace_filter; /* clear filter */ + } + + /* count user IDs in order to allocate sufficient memory */ + size = 1; + residual = val; + while ((residual = strchr(residual, ',')) != NULL) { + residual++; + size++; + } + + /* check if the specified list exceeds the filter limit */ + if (size > MAX_VMID_FILTER) + return -ENOSPC; + + array = kzalloc(size * 8, GFP_KERNEL); + if (!array) + return -ENOMEM; + + count = size; + residual = val; + while (*residual && count) { + residual = hvc_iucv_parse_filter(residual, + array + ((size - count) * 8)); + if (IS_ERR(residual)) { + err = PTR_ERR(residual); + kfree(array); + goto out_err; + } + count--; + } + +out_replace_filter: + write_lock_bh(&hvc_iucv_filter_lock); + old_filter = hvc_iucv_filter; + hvc_iucv_filter_size = size; + hvc_iucv_filter = array; + write_unlock_bh(&hvc_iucv_filter_lock); + kfree(old_filter); + + err = 0; +out_err: + return err; +} + +/** + * param_set_vmidfilter() - Set z/VM user ID filter parameter + * @val: String consisting of a comma-separated list of z/VM user IDs + * @kp: Kernel parameter pointing to hvc_iucv_filter array + * + * The function sets up the z/VM user ID filter specified as comma-separated + * list of user IDs in @val. + * Note: If it is called early in the boot process, @val is stored and + * parsed later in hvc_iucv_init(). + */ +static int param_set_vmidfilter(const char *val, struct kernel_param *kp) +{ + int rc; + + if (!MACHINE_IS_VM || !hvc_iucv_devices) + return -ENODEV; + + if (!val) + return -EINVAL; + + rc = 0; + if (slab_is_available()) + rc = hvc_iucv_setup_filter(val); + else + hvc_iucv_filter_string = val; /* defer... */ + return rc; +} + +/** + * param_get_vmidfilter() - Get z/VM user ID filter + * @buffer: Buffer to store z/VM user ID filter, + * (buffer size assumption PAGE_SIZE) + * @kp: Kernel parameter pointing to the hvc_iucv_filter array + * + * The function stores the filter as a comma-separated list of z/VM user IDs + * in @buffer. Typically, sysfs routines call this function for attr show. + */ +static int param_get_vmidfilter(char *buffer, struct kernel_param *kp) +{ + int rc; + size_t index, len; + void *start, *end; + + if (!MACHINE_IS_VM || !hvc_iucv_devices) + return -ENODEV; + + rc = 0; + read_lock_bh(&hvc_iucv_filter_lock); + for (index = 0; index < hvc_iucv_filter_size; index++) { + start = hvc_iucv_filter + (8 * index); + end = memchr(start, ' ', 8); + len = (end) ? end - start : 8; + memcpy(buffer + rc, start, len); + rc += len; + buffer[rc++] = ','; + } + read_unlock_bh(&hvc_iucv_filter_lock); + if (rc) + buffer[--rc] = '\0'; /* replace last comma and update rc */ + return rc; +} + +#define param_check_vmidfilter(name, p) __param_check(name, p, void) + /** * hvc_iucv_init() - z/VM IUCV HVC device driver initialization */ @@ -884,19 +1092,44 @@ static int __init hvc_iucv_init(void) int rc; unsigned int i; + if (!hvc_iucv_devices) + return -ENODEV; + if (!MACHINE_IS_VM) { pr_notice("The z/VM IUCV HVC device driver cannot " "be used without z/VM\n"); - return -ENODEV; + rc = -ENODEV; + goto out_error; } - if (!hvc_iucv_devices) - return -ENODEV; - if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) { pr_err("%lu is not a valid value for the hvc_iucv= " "kernel parameter\n", hvc_iucv_devices); - return -EINVAL; + rc = -EINVAL; + goto out_error; + } + + /* parse hvc_iucv_allow string and create z/VM user ID filter list */ + if (hvc_iucv_filter_string) { + rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); + switch (rc) { + case 0: + break; + case -ENOMEM: + pr_err("Allocating memory failed with " + "reason code=%d\n", 3); + goto out_error; + case -EINVAL: + pr_err("hvc_iucv_allow= does not specify a valid " + "z/VM user ID list\n"); + goto out_error; + case -ENOSPC: + pr_err("hvc_iucv_allow= specifies too many " + "z/VM user IDs\n"); + goto out_error; + default: + goto out_error; + } } hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT, @@ -904,7 +1137,8 @@ static int __init hvc_iucv_init(void) 0, 0, NULL); if (!hvc_iucv_buffer_cache) { pr_err("Allocating memory failed with reason code=%d\n", 1); - return -ENOMEM; + rc = -ENOMEM; + goto out_error; } hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR, @@ -912,7 +1146,8 @@ static int __init hvc_iucv_init(void) if (!hvc_iucv_mempool) { pr_err("Allocating memory failed with reason code=%d\n", 2); kmem_cache_destroy(hvc_iucv_buffer_cache); - return -ENOMEM; + rc = -ENOMEM; + goto out_error; } /* register the first terminal device as console @@ -956,6 +1191,8 @@ out_error_hvc: out_error_memory: mempool_destroy(hvc_iucv_mempool); kmem_cache_destroy(hvc_iucv_buffer_cache); +out_error: + hvc_iucv_devices = 0; /* ensure that we do not provide any device */ return rc; } @@ -971,3 +1208,4 @@ static int __init hvc_iucv_config(char *val) device_initcall(hvc_iucv_init); __setup("hvc_iucv=", hvc_iucv_config); +core_param(hvc_iucv_allow, hvc_iucv_filter, vmidfilter, 0640); -- cgit v1.2.3-59-g8ed1b From 7676b8fd701beb47cc1b4cc291acaa07287ea69a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 26 Mar 2009 20:47:00 +0000 Subject: dontdiff: Fix asm exclude Now that the headers are in arch/foo/include/asm we don't want to exclude them when preparing diff files. Closes-bug: 12921 Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- Documentation/dontdiff | 1 - 1 file changed, 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/dontdiff b/Documentation/dontdiff index 1e89a51ea49b..88519daab6e9 100644 --- a/Documentation/dontdiff +++ b/Documentation/dontdiff @@ -62,7 +62,6 @@ aic7*reg_print.c* aic7*seq.h* aicasm aicdb.h* -asm asm-offsets.h asm_offsets.h autoconf.h* -- cgit v1.2.3-59-g8ed1b From df4b6806d35ca6adedd5c0a7e36ac74af1c32d7a Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 11 Mar 2009 19:22:04 -0500 Subject: powerpc: clean up ssi.txt, add definition for fsl,ssi-asynchronous Add the definition of the fsl,ssi-asynchronous property to ssi.txt (documentation of the device tree bindings for the Freescale SSI device). Also tidy up the layout of ssi.txt. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- Documentation/powerpc/dts-bindings/fsl/ssi.txt | 68 +++++++++++++++----------- 1 file changed, 39 insertions(+), 29 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/dts-bindings/fsl/ssi.txt b/Documentation/powerpc/dts-bindings/fsl/ssi.txt index 731332288931..5ff76c9c57d2 100644 --- a/Documentation/powerpc/dts-bindings/fsl/ssi.txt +++ b/Documentation/powerpc/dts-bindings/fsl/ssi.txt @@ -4,46 +4,56 @@ The SSI is a serial device that communicates with audio codecs. It can be programmed in AC97, I2S, left-justified, or right-justified modes. Required properties: -- compatible : compatible list, containing "fsl,ssi" -- cell-index : the SSI, <0> = SSI1, <1> = SSI2, and so on -- reg : offset and length of the register set for the device -- interrupts : where a is the interrupt number and b is a - field that represents an encoding of the sense and - level information for the interrupt. This should be - encoded based on the information in section 2) - depending on the type of interrupt controller you - have. -- interrupt-parent : the phandle for the interrupt controller that - services interrupts for this device. -- fsl,mode : the operating mode for the SSI interface - "i2s-slave" - I2S mode, SSI is clock slave - "i2s-master" - I2S mode, SSI is clock master - "lj-slave" - left-justified mode, SSI is clock slave - "lj-master" - l.j. mode, SSI is clock master - "rj-slave" - right-justified mode, SSI is clock slave - "rj-master" - r.j., SSI is clock master - "ac97-slave" - AC97 mode, SSI is clock slave - "ac97-master" - AC97 mode, SSI is clock master -- fsl,playback-dma: phandle to a node for the DMA channel to use for +- compatible: Compatible list, contains "fsl,ssi". +- cell-index: The SSI, <0> = SSI1, <1> = SSI2, and so on. +- reg: Offset and length of the register set for the device. +- interrupts: where a is the interrupt number and b is a + field that represents an encoding of the sense and + level information for the interrupt. This should be + encoded based on the information in section 2) + depending on the type of interrupt controller you + have. +- interrupt-parent: The phandle for the interrupt controller that + services interrupts for this device. +- fsl,mode: The operating mode for the SSI interface. + "i2s-slave" - I2S mode, SSI is clock slave + "i2s-master" - I2S mode, SSI is clock master + "lj-slave" - left-justified mode, SSI is clock slave + "lj-master" - l.j. mode, SSI is clock master + "rj-slave" - right-justified mode, SSI is clock slave + "rj-master" - r.j., SSI is clock master + "ac97-slave" - AC97 mode, SSI is clock slave + "ac97-master" - AC97 mode, SSI is clock master +- fsl,playback-dma: Phandle to a node for the DMA channel to use for playback of audio. This is typically dictated by SOC design. See the notes below. -- fsl,capture-dma: phandle to a node for the DMA channel to use for +- fsl,capture-dma: Phandle to a node for the DMA channel to use for capture (recording) of audio. This is typically dictated by SOC design. See the notes below. -- fsl,fifo-depth: the number of elements in the transmit and receive FIFOs. +- fsl,fifo-depth: The number of elements in the transmit and receive FIFOs. This number is the maximum allowed value for SFCSR[TFWM0]. +- fsl,ssi-asynchronous: + If specified, the SSI is to be programmed in asynchronous + mode. In this mode, pins SRCK, STCK, SRFS, and STFS must + all be connected to valid signals. In synchronous mode, + SRCK and SRFS are ignored. Asynchronous mode allows + playback and capture to use different sample sizes and + sample rates. Some drivers may require that SRCK and STCK + be connected together, and SRFS and STFS be connected + together. This would still allow different sample sizes, + but not different sample rates. Optional properties: -- codec-handle : phandle to a 'codec' node that defines an audio - codec connected to this SSI. This node is typically - a child of an I2C or other control node. +- codec-handle: Phandle to a 'codec' node that defines an audio + codec connected to this SSI. This node is typically + a child of an I2C or other control node. Child 'codec' node required properties: -- compatible : compatible list, contains the name of the codec +- compatible: Compatible list, contains the name of the codec Child 'codec' node optional properties: -- clock-frequency : The frequency of the input clock, which typically - comes from an on-board dedicated oscillator. +- clock-frequency: The frequency of the input clock, which typically comes + from an on-board dedicated oscillator. Notes on fsl,playback-dma and fsl,capture-dma: -- cgit v1.2.3-59-g8ed1b From 6ee7d33056f6e6fc7437d980dcc741816deedd0f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 20 Mar 2009 23:53:06 -0400 Subject: cfg80211: make regdom module parameter available oustide of OLD_REG It seems a few users are using this module parameter although its not recommended. People are finding it useful despite there being utilities for setting this in userspace. I'm not aware of any distribution using this though. Until userspace and distributions catch up with a default userspace automatic replacement (GeoClue integration would be nirvana) we copy the ieee80211_regdom module parameter from OLD_REG to the new reg code to help these users migrate. Users who are using the non-valid ISO / IEC 3166 alpha "EU" in their ieee80211_regdom module parameter and migrate to non-OLD_REG enabled system will world roam. This also schedules removal of this same ieee80211_regdom module parameter circa March 2010. Hope is by then nirvana is reached and users will abandoned the module parameter completely. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- Documentation/feature-removal-schedule.txt | 30 ++++++++++++++++++++++++++---- net/wireless/reg.c | 7 ++++++- 2 files changed, 32 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index e47c0ff8ba7a..8365f52a3548 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -6,7 +6,31 @@ be removed from this file. --------------------------- -What: old static regulatory information and ieee80211_regdom module parameter +What: The ieee80211_regdom module parameter +When: March 2010 + +Why: This was inherited by the CONFIG_WIRELESS_OLD_REGULATORY code, + and currently serves as an option for users to define an + ISO / IEC 3166 alpha2 code for the country they are currently + present in. Although there are userspace API replacements for this + through nl80211 distributions haven't yet caught up with implementing + decent alternatives through standard GUIs. Although available as an + option through iw or wpa_supplicant its just a matter of time before + distributions pick up good GUI options for this. The ideal solution + would actually consist of intelligent designs which would do this for + the user automatically even when travelling through different countries. + Until then we leave this module parameter as a compromise. + + When userspace improves with reasonable widely-available alternatives for + this we will no longer need this module parameter. This entry hopes that + by the super-futuristically looking date of "March 2010" we will have + such replacements widely available. + +Who: Luis R. Rodriguez + +--------------------------- + +What: old static regulatory information When: 2.6.29 Why: The old regulatory infrastructure has been replaced with a new one which does not require statically defined regulatory domains. We do @@ -17,9 +41,7 @@ Why: The old regulatory infrastructure has been replaced with a new one * JP * EU and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was - set. We also kept around the ieee80211_regdom module parameter in case - some applications were relying on it. Changing regulatory domains - can now be done instead by using nl80211, as is done with iw. + set. Who: Luis R. Rodriguez --------------------------- diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9afc9168748b..ac048a158d85 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -122,9 +122,14 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = #ifdef CONFIG_WIRELESS_OLD_REGULATORY static char *ieee80211_regdom = "US"; +#else +static char *ieee80211_regdom = "00"; +#endif + module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); +#ifdef CONFIG_WIRELESS_OLD_REGULATORY /* * We assume 40 MHz bandwidth for the old regulatory work. * We make emphasis we are using the exact same frequencies @@ -2152,7 +2157,7 @@ int regulatory_init(void) #else cfg80211_regdomain = cfg80211_world_regdom; - err = regulatory_hint_core("00"); + err = regulatory_hint_core(ieee80211_regdom); #endif if (err) { if (err == -ENOMEM) -- cgit v1.2.3-59-g8ed1b From 04de83815993714a7ba2618f637fa1092a5f664b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sun, 22 Mar 2009 21:57:35 +0200 Subject: mac80211: add beacon filtering support Add IEEE80211_HW_BEACON_FILTERING flag so that driver inform that it supports beacon filtering. Drivers need to call the new function ieee80211_beacon_loss() to notify about beacon loss. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- Documentation/DocBook/mac80211.tmpl | 6 +++++ include/net/mac80211.h | 33 +++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 3 +++ net/mac80211/mlme.c | 49 ++++++++++++++++++++++++++++++++++++- 5 files changed, 92 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl index 8af6d9626878..fbeaffc1dcc3 100644 --- a/Documentation/DocBook/mac80211.tmpl +++ b/Documentation/DocBook/mac80211.tmpl @@ -227,6 +227,12 @@ usage should require reading the full document. !Pinclude/net/mac80211.h Powersave support + + Beacon filter support +!Pinclude/net/mac80211.h Beacon filter support +!Finclude/net/mac80211.h ieee80211_beacon_loss + + Multiple queues and QoS support TBD diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 174dc1d7526b..d881ed8ad2c1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -882,6 +882,10 @@ enum ieee80211_tkip_key_type { * * @IEEE80211_HW_MFP_CAPABLE: * Hardware supports management frame protection (MFP, IEEE 802.11w). + * + * @IEEE80211_HW_BEACON_FILTER: + * Hardware supports dropping of irrelevant beacon frames to + * avoid waking up cpu. */ enum ieee80211_hw_flags { IEEE80211_HW_RX_INCLUDES_FCS = 1<<1, @@ -897,6 +901,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11, IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, IEEE80211_HW_MFP_CAPABLE = 1<<13, + IEEE80211_HW_BEACON_FILTER = 1<<14, }; /** @@ -1120,6 +1125,24 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * value, or by the stack if all nullfunc handling is in the stack. */ +/** + * DOC: Beacon filter support + * + * Some hardware have beacon filter support to reduce host cpu wakeups + * which will reduce system power consumption. It usuallly works so that + * the firmware creates a checksum of the beacon but omits all constantly + * changing elements (TSF, TIM etc). Whenever the checksum changes the + * beacon is forwarded to the host, otherwise it will be just dropped. That + * way the host will only receive beacons where some relevant information + * (for example ERP protection or WMM settings) have changed. + * + * Beacon filter support is informed with %IEEE80211_HW_BEACON_FILTER flag. + * The driver needs to enable beacon filter support whenever power save is + * enabled, that is %IEEE80211_CONF_PS is set. When power save is enabled, + * the stack will not check for beacon miss at all and the driver needs to + * notify about complete loss of beacons with ieee80211_beacon_loss(). + */ + /** * DOC: Frame filtering * @@ -1970,6 +1993,16 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra, struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw, const u8 *addr); +/** + * ieee80211_beacon_loss - inform hardware does not receive beacons + * + * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf. + * + * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and + * IEEE80211_CONF_PS is set, the driver needs to inform whenever the + * hardware is not receiving beacons with this function. + */ +void ieee80211_beacon_loss(struct ieee80211_vif *vif); /* Rate control API */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8a617a7fc090..acba78e1a5ca 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -275,6 +275,7 @@ struct ieee80211_if_managed { struct timer_list chswitch_timer; struct work_struct work; struct work_struct chswitch_work; + struct work_struct beacon_loss_work; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; @@ -1086,6 +1087,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, int powersave); void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); +void ieee80211_beacon_loss_work(struct work_struct *work); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index dd2a276fa8ca..91e8e1bacaaa 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -477,6 +477,9 @@ static int ieee80211_stop(struct net_device *dev) */ cancel_work_sync(&sdata->u.mgd.work); cancel_work_sync(&sdata->u.mgd.chswitch_work); + + cancel_work_sync(&sdata->u.mgd.beacon_loss_work); + /* * When we get here, the interface is marked down. * Call synchronize_rcu() to wait for the RX path diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8f30f4d19da0..7ecda9d59d8a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -610,6 +610,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= ieee80211_handle_bss_capability(sdata, bss->cbss.capability, bss->has_erp_value, bss->erp_value); + cfg80211_hold_bss(&bss->cbss); + ieee80211_rx_bss_put(local, bss); } @@ -751,6 +753,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; + struct ieee80211_conf *conf = &local_to_hw(local)->conf; + struct ieee80211_bss *bss; struct sta_info *sta; u32 changed = 0, config_changed = 0; @@ -774,6 +778,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_sta_tear_down_BA_sessions(sta); + bss = ieee80211_rx_bss_get(local, ifmgd->bssid, + conf->channel->center_freq, + ifmgd->ssid, ifmgd->ssid_len); + + if (bss) { + cfg80211_unhold_bss(&bss->cbss); + ieee80211_rx_bss_put(local, bss); + } + if (self_disconnected) { if (deauth) ieee80211_send_deauth_disassoc(sdata, @@ -925,6 +938,33 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, jiffies + IEEE80211_MONITORING_INTERVAL); } +void ieee80211_beacon_loss_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.beacon_loss_work); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + + printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM " + "- sending probe request\n", sdata->dev->name, + sdata->u.mgd.bssid); + + ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; + ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, + ifmgd->ssid_len, NULL, 0); + + mod_timer(&ifmgd->timer, jiffies + IEEE80211_MONITORING_INTERVAL); +} + +void ieee80211_beacon_loss(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + queue_work(sdata->local->hw.workqueue, + &sdata->u.mgd.beacon_loss_work); +} +EXPORT_SYMBOL(ieee80211_beacon_loss); + static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -959,7 +999,13 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) goto unlock; } - if (time_after(jiffies, + /* + * Beacon filtering is only enabled with power save and then the + * stack should not check for beacon loss. + */ + if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) && + (local->hw.conf.flags & IEEE80211_CONF_PS)) && + time_after(jiffies, ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) { printk(KERN_DEBUG "%s: beacon loss from AP %pM " "- sending probe request\n", @@ -1869,6 +1915,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd = &sdata->u.mgd; INIT_WORK(&ifmgd->work, ieee80211_sta_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); + INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, -- cgit v1.2.3-59-g8ed1b From 8a5117d80fe93de5df5b56480054f7df1fd20755 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 24 Mar 2009 21:21:07 -0400 Subject: cfg80211: default CONFIG_WIRELESS_OLD_REGULATORY to n And update description and feature-removal schedule according to the new plan. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- Documentation/feature-removal-schedule.txt | 13 +++++++---- net/wireless/Kconfig | 37 ++++++++---------------------- 2 files changed, 18 insertions(+), 32 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 8365f52a3548..02ea3773535e 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -7,7 +7,7 @@ be removed from this file. --------------------------- What: The ieee80211_regdom module parameter -When: March 2010 +When: March 2010 / desktop catchup Why: This was inherited by the CONFIG_WIRELESS_OLD_REGULATORY code, and currently serves as an option for users to define an @@ -30,18 +30,23 @@ Who: Luis R. Rodriguez --------------------------- -What: old static regulatory information -When: 2.6.29 +What: CONFIG_WIRELESS_OLD_REGULATORY - old static regulatory information +When: March 2010 / desktop catchup + Why: The old regulatory infrastructure has been replaced with a new one which does not require statically defined regulatory domains. We do not want to keep static regulatory domains in the kernel due to the the dynamic nature of regulatory law and localization. We kept around the old static definitions for the regulatory domains of: + * US * JP * EU + and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was - set. + set. We will remove this option once the standard Linux desktop catches + up with the new userspace APIs we have implemented. + Who: Luis R. Rodriguez --------------------------- diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index d1d18f34d272..3c3bc9e579ed 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -12,36 +12,17 @@ config CFG80211_REG_DEBUG config WIRELESS_OLD_REGULATORY bool "Old wireless static regulatory definitions" - default y + default n ---help--- This option enables the old static regulatory information - and uses it within the new framework. This is available - temporarily as an option to help prevent immediate issues - due to the switch to the new regulatory framework which - does require a new userspace application which has the - database of regulatory information (CRDA) and another for - setting regulatory domains (iw). - - For more information see: - - http://wireless.kernel.org/en/developers/Regulatory/CRDA - http://wireless.kernel.org/en/users/Documentation/iw - - It is important to note though that if you *do* have CRDA present - and if this option is enabled CRDA *will* be called to update the - regulatory domain (for US and JP only). Support for letting the user - set the regulatory domain through iw is also supported. This option - mainly exists to leave around for a kernel release some old static - regulatory domains that were defined and to keep around the old - ieee80211_regdom module parameter. This is being phased out and you - should stop using them ASAP. - - Note: You will need CRDA if you want 802.11d support - - Say Y unless you have installed a new userspace application. - Also say Y if have one currently depending on the ieee80211_regdom - module parameter and cannot port it to use the new userspace - interfaces. + and uses it within the new framework. This option is available + for historical reasons and it is advised to leave it off. + + For details see: + + http://wireless.kernel.org/en/developers/Regulatory + + Say N and if you say Y, please tell us why. The default is N. config WIRELESS_EXT bool "Wireless extensions" -- cgit v1.2.3-59-g8ed1b From 58bfbb51ff2b0fdc6c732ff3d72f50aa632b67a2 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 27 Mar 2009 17:10:41 -0400 Subject: selinux: Remove the "compat_net" compatibility code The SELinux "compat_net" is marked as deprecated, the time has come to finally remove it from the kernel. Further code simplifications are likely in the future, but this patch was intended to be a simple, straight-up removal of the compat_net code. Signed-off-by: Paul Moore Signed-off-by: James Morris --- Documentation/feature-removal-schedule.txt | 11 --- Documentation/kernel-parameters.txt | 9 -- security/selinux/hooks.c | 153 ++--------------------------- security/selinux/selinuxfs.c | 68 ------------- 4 files changed, 7 insertions(+), 234 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 02ea3773535e..049a96247f58 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -355,17 +355,6 @@ Who: Hans de Goede --------------------------- -What: SELinux "compat_net" functionality -When: 2.6.30 at the earliest -Why: In 2.6.18 the Secmark concept was introduced to replace the "compat_net" - network access control functionality of SELinux. Secmark offers both - better performance and greater flexibility than the "compat_net" - mechanism. Now that the major Linux distributions have moved to - Secmark, it is time to deprecate the older mechanism and start the - process of removing the old code. -Who: Paul Moore ---------------------------- - What: sysfs ui for changing p4-clockmod parameters When: September 2009 Why: See commits 129f8ae9b1b5be94517da76009ea956e89104ce8 and diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index fa4e1239a8fa..d1b082772e39 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2019,15 +2019,6 @@ and is between 256 and 4096 characters. It is defined in the file If enabled at boot time, /selinux/disable can be used later to disable prior to initial policy load. - selinux_compat_net = - [SELINUX] Set initial selinux_compat_net flag value. - Format: { "0" | "1" } - 0 -- use new secmark-based packet controls - 1 -- use legacy packet controls - Default value is 0 (preferred). - Value can be changed at runtime via - /selinux/compat_net. - serialnumber [BUGS=X86-32] shapers= [NET] diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ee2e781d11d7..ba808ef6babb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -93,7 +93,6 @@ extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); -extern int selinux_compat_net; extern struct security_operations *security_ops; /* SECMARK reference count */ @@ -4019,72 +4018,6 @@ static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, SECCLASS_NODE, NODE__RECVFROM, ad); } -static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, - struct sk_buff *skb, - struct avc_audit_data *ad, - u16 family, - char *addrp) -{ - int err; - struct sk_security_struct *sksec = sk->sk_security; - u16 sk_class; - u32 netif_perm, node_perm, recv_perm; - u32 port_sid, node_sid, if_sid, sk_sid; - - sk_sid = sksec->sid; - sk_class = sksec->sclass; - - switch (sk_class) { - case SECCLASS_UDP_SOCKET: - netif_perm = NETIF__UDP_RECV; - node_perm = NODE__UDP_RECV; - recv_perm = UDP_SOCKET__RECV_MSG; - break; - case SECCLASS_TCP_SOCKET: - netif_perm = NETIF__TCP_RECV; - node_perm = NODE__TCP_RECV; - recv_perm = TCP_SOCKET__RECV_MSG; - break; - case SECCLASS_DCCP_SOCKET: - netif_perm = NETIF__DCCP_RECV; - node_perm = NODE__DCCP_RECV; - recv_perm = DCCP_SOCKET__RECV_MSG; - break; - default: - netif_perm = NETIF__RAWIP_RECV; - node_perm = NODE__RAWIP_RECV; - recv_perm = 0; - break; - } - - err = sel_netif_sid(skb->iif, &if_sid); - if (err) - return err; - err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); - if (err) - return err; - - err = sel_netnode_sid(addrp, family, &node_sid); - if (err) - return err; - err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad); - if (err) - return err; - - if (!recv_perm) - return 0; - err = sel_netport_sid(sk->sk_protocol, - ntohs(ad->u.net.sport), &port_sid); - if (unlikely(err)) { - printk(KERN_WARNING - "SELinux: failure in" - " selinux_sock_rcv_skb_iptables_compat()," - " network port label not found\n"); - return err; - } - return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad); -} - static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, u16 family) { @@ -4102,14 +4035,12 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, if (err) return err; - if (selinux_compat_net) - err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad, - family, addrp); - else if (selinux_secmark_enabled()) + if (selinux_secmark_enabled()) { err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, PACKET__RECV, &ad); - if (err) - return err; + if (err) + return err; + } if (selinux_policycap_netpeer) { err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); @@ -4151,7 +4082,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * to the selinux_sock_rcv_skb_compat() function to deal with the * special handling. We do this in an attempt to keep this function * as fast and as clean as possible. */ - if (selinux_compat_net || !selinux_policycap_netpeer) + if (!selinux_policycap_netpeer) return selinux_sock_rcv_skb_compat(sk, skb, family); secmark_active = selinux_secmark_enabled(); @@ -4516,71 +4447,6 @@ static unsigned int selinux_ipv4_output(unsigned int hooknum, return selinux_ip_output(skb, PF_INET); } -static int selinux_ip_postroute_iptables_compat(struct sock *sk, - int ifindex, - struct avc_audit_data *ad, - u16 family, char *addrp) -{ - int err; - struct sk_security_struct *sksec = sk->sk_security; - u16 sk_class; - u32 netif_perm, node_perm, send_perm; - u32 port_sid, node_sid, if_sid, sk_sid; - - sk_sid = sksec->sid; - sk_class = sksec->sclass; - - switch (sk_class) { - case SECCLASS_UDP_SOCKET: - netif_perm = NETIF__UDP_SEND; - node_perm = NODE__UDP_SEND; - send_perm = UDP_SOCKET__SEND_MSG; - break; - case SECCLASS_TCP_SOCKET: - netif_perm = NETIF__TCP_SEND; - node_perm = NODE__TCP_SEND; - send_perm = TCP_SOCKET__SEND_MSG; - break; - case SECCLASS_DCCP_SOCKET: - netif_perm = NETIF__DCCP_SEND; - node_perm = NODE__DCCP_SEND; - send_perm = DCCP_SOCKET__SEND_MSG; - break; - default: - netif_perm = NETIF__RAWIP_SEND; - node_perm = NODE__RAWIP_SEND; - send_perm = 0; - break; - } - - err = sel_netif_sid(ifindex, &if_sid); - if (err) - return err; - err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); - return err; - - err = sel_netnode_sid(addrp, family, &node_sid); - if (err) - return err; - err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad); - if (err) - return err; - - if (send_perm != 0) - return 0; - - err = sel_netport_sid(sk->sk_protocol, - ntohs(ad->u.net.dport), &port_sid); - if (unlikely(err)) { - printk(KERN_WARNING - "SELinux: failure in" - " selinux_ip_postroute_iptables_compat()," - " network port label not found\n"); - return err; - } - return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad); -} - static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, int ifindex, u16 family) @@ -4601,15 +4467,10 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) return NF_DROP; - if (selinux_compat_net) { - if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex, - &ad, family, addrp)) - return NF_DROP; - } else if (selinux_secmark_enabled()) { + if (selinux_secmark_enabled()) if (avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, PACKET__SEND, &ad)) return NF_DROP; - } if (selinux_policycap_netpeer) if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto)) @@ -4633,7 +4494,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, * to the selinux_ip_postroute_compat() function to deal with the * special handling. We do this in an attempt to keep this function * as fast and as clean as possible. */ - if (selinux_compat_net || !selinux_policycap_netpeer) + if (!selinux_policycap_netpeer) return selinux_ip_postroute_compat(skb, ifindex, family); #ifdef CONFIG_XFRM /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index d3c8b982cfb0..2d5136ec3d54 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -47,8 +47,6 @@ static char *policycap_names[] = { unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; -int selinux_compat_net = 0; - static int __init checkreqprot_setup(char *str) { unsigned long checkreqprot; @@ -58,16 +56,6 @@ static int __init checkreqprot_setup(char *str) } __setup("checkreqprot=", checkreqprot_setup); -static int __init selinux_compat_net_setup(char *str) -{ - unsigned long compat_net; - if (!strict_strtoul(str, 0, &compat_net)) - selinux_compat_net = compat_net ? 1 : 0; - return 1; -} -__setup("selinux_compat_net=", selinux_compat_net_setup); - - static DEFINE_MUTEX(sel_mutex); /* global data for booleans */ @@ -450,61 +438,6 @@ static const struct file_operations sel_checkreqprot_ops = { .write = sel_write_checkreqprot, }; -static ssize_t sel_read_compat_net(struct file *filp, char __user *buf, - size_t count, loff_t *ppos) -{ - char tmpbuf[TMPBUFLEN]; - ssize_t length; - - length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_compat_net); - return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); -} - -static ssize_t sel_write_compat_net(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - char *page; - ssize_t length; - int new_value; - - length = task_has_security(current, SECURITY__LOAD_POLICY); - if (length) - return length; - - if (count >= PAGE_SIZE) - return -ENOMEM; - if (*ppos != 0) { - /* No partial writes. */ - return -EINVAL; - } - page = (char *)get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - length = -EFAULT; - if (copy_from_user(page, buf, count)) - goto out; - - length = -EINVAL; - if (sscanf(page, "%d", &new_value) != 1) - goto out; - - if (new_value) { - printk(KERN_NOTICE - "SELinux: compat_net is deprecated, please use secmark" - " instead\n"); - selinux_compat_net = 1; - } else - selinux_compat_net = 0; - length = count; -out: - free_page((unsigned long) page); - return length; -} -static const struct file_operations sel_compat_net_ops = { - .read = sel_read_compat_net, - .write = sel_write_compat_net, -}; - /* * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c */ @@ -1665,7 +1598,6 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, - [SEL_COMPAT_NET] = {"compat_net", &sel_compat_net_ops, S_IRUGO|S_IWUSR}, [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, /* last one */ {""} -- cgit v1.2.3-59-g8ed1b From 4303154e86597885bc3cbc178a48ccbc8213875f Mon Sep 17 00:00:00 2001 From: Etienne Basset Date: Fri, 27 Mar 2009 17:11:01 -0400 Subject: smack: Add a new '-CIPSO' option to the network address label configuration This patch adds a new special option '-CIPSO' to the Smack subsystem. When used in the netlabel list, it means "use CIPSO networking". A use case is when your local network speaks CIPSO and you want also to connect to the unlabeled Internet. This patch also add some documentation describing that. The patch also corrects an oops when setting a '' SMACK64 xattr to a file. Signed-off-by: Etienne Basset Signed-off-by: Paul Moore Acked-by: Casey Schaufler Signed-off-by: James Morris --- Documentation/Smack.txt | 42 +++++++++++++++++++++++++++++++++++++----- security/smack/smack.h | 3 +++ security/smack/smack_access.c | 3 +++ security/smack/smack_lsm.c | 11 +++++++++-- security/smack/smackfs.c | 38 ++++++++++++++++++++++++++++++-------- 5 files changed, 82 insertions(+), 15 deletions(-) (limited to 'Documentation') diff --git a/Documentation/Smack.txt b/Documentation/Smack.txt index 989c2fcd8111..629c92e99783 100644 --- a/Documentation/Smack.txt +++ b/Documentation/Smack.txt @@ -184,14 +184,16 @@ length. Single character labels using special characters, that being anything other than a letter or digit, are reserved for use by the Smack development team. Smack labels are unstructured, case sensitive, and the only operation ever performed on them is comparison for equality. Smack labels cannot -contain unprintable characters or the "/" (slash) character. +contain unprintable characters or the "/" (slash) character. Smack labels +cannot begin with a '-', which is reserved for special options. There are some predefined labels: - _ Pronounced "floor", a single underscore character. - ^ Pronounced "hat", a single circumflex character. - * Pronounced "star", a single asterisk character. - ? Pronounced "huh", a single question mark character. + _ Pronounced "floor", a single underscore character. + ^ Pronounced "hat", a single circumflex character. + * Pronounced "star", a single asterisk character. + ? Pronounced "huh", a single question mark character. + @ Pronounced "Internet", a single at sign character. Every task on a Smack system is assigned a label. System tasks, such as init(8) and systems daemons, are run with the floor ("_") label. User tasks @@ -412,6 +414,36 @@ sockets. A privileged program may set this to match the label of another task with which it hopes to communicate. +Smack Netlabel Exceptions + +You will often find that your labeled application has to talk to the outside, +unlabeled world. To do this there's a special file /smack/netlabel where you can +add some exceptions in the form of : +@IP1 LABEL1 or +@IP2/MASK LABEL2 + +It means that your application will have unlabeled access to @IP1 if it has +write access on LABEL1, and access to the subnet @IP2/MASK if it has write +access on LABEL2. + +Entries in the /smack/netlabel file are matched by longest mask first, like in +classless IPv4 routing. + +A special label '@' and an option '-CIPSO' can be used there : +@ means Internet, any application with any label has access to it +-CIPSO means standard CIPSO networking + +If you don't know what CIPSO is and don't plan to use it, you can just do : +echo 127.0.0.1 -CIPSO > /smack/netlabel +echo 0.0.0.0/0 @ > /smack/netlabel + +If you use CIPSO on your 192.168.0.0/16 local network and need also unlabeled +Internet access, you can have : +echo 127.0.0.1 -CIPSO > /smack/netlabel +echo 192.168.0.0/16 -CIPSO > /smack/netlabel +echo 0.0.0.0/0 @ > /smack/netlabel + + Writing Applications for Smack There are three sorts of applications that will run on a Smack system. How an diff --git a/security/smack/smack.h b/security/smack/smack.h index 5e5a3bcb599a..42ef313f9856 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -132,6 +132,8 @@ struct smack_known { #define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT +#define SMACK_CIPSO_OPTION "-CIPSO" + /* * How communications on this socket are treated. * Usually it's determined by the underlying netlabel code @@ -199,6 +201,7 @@ u32 smack_to_secid(const char *); extern int smack_cipso_direct; extern char *smack_net_ambient; extern char *smack_onlycap; +extern const char *smack_cipso_option; extern struct smack_known smack_known_floor; extern struct smack_known smack_known_hat; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 58564195bb09..ac0a2707f6d4 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -261,6 +261,9 @@ char *smk_import(const char *string, int len) { struct smack_known *skp; + /* labels cannot begin with a '-' */ + if (string[0] == '-') + return NULL; skp = smk_import_entry(string, len); if (skp == NULL) return NULL; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8ed502c2ad45..921514902eca 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -609,6 +609,9 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) { if (!capable(CAP_MAC_ADMIN)) rc = -EPERM; + /* a label cannot be void and cannot begin with '-' */ + if (size == 0 || (size > 0 && ((char *)value)[0] == '-')) + rc = -EINVAL; } else rc = cap_inode_setxattr(dentry, name, value, size, flags); @@ -1323,8 +1326,12 @@ static char *smack_host_label(struct sockaddr_in *sip) * so we have found the most specific match */ if ((&snp->smk_host.sin_addr)->s_addr == - (siap->s_addr & (&snp->smk_mask)->s_addr)) + (siap->s_addr & (&snp->smk_mask)->s_addr)) { + /* we have found the special CIPSO option */ + if (snp->smk_label == smack_cipso_option) + return NULL; return snp->smk_label; + } return NULL; } @@ -1486,7 +1493,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, struct socket *sock; int rc = 0; - if (value == NULL || size > SMK_LABELLEN) + if (value == NULL || size > SMK_LABELLEN || size == 0) return -EACCES; sp = smk_import(value, size); diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 856c8a287523..e03a7e19c73b 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -86,6 +86,9 @@ LIST_HEAD(smack_rule_list); static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; +const char *smack_cipso_option = SMACK_CIPSO_OPTION; + + #define SEQ_READ_FINISHED 1 /* @@ -565,6 +568,11 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, goto unlockedout; } + /* labels cannot begin with a '-' */ + if (data[0] == '-') { + rc = -EINVAL; + goto unlockedout; + } data[count] = '\0'; rule = data; /* @@ -808,9 +816,18 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, if (m > BEBITS) return -EINVAL; - sp = smk_import(smack, 0); - if (sp == NULL) - return -EINVAL; + /* if smack begins with '-', its an option, don't import it */ + if (smack[0] != '-') { + sp = smk_import(smack, 0); + if (sp == NULL) + return -EINVAL; + } else { + /* check known options */ + if (strcmp(smack, smack_cipso_option) == 0) + sp = (char *)smack_cipso_option; + else + return -EINVAL; + } for (temp_mask = 0; m > 0; m--) { temp_mask |= mask_bits; @@ -849,18 +866,23 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, smk_netlbladdr_insert(skp); } } else { - rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, - &skp->smk_host.sin_addr, &skp->smk_mask, - PF_INET, &audit_info); + /* we delete the unlabeled entry, only if the previous label + * wasnt the special CIPSO option */ + if (skp->smk_label != smack_cipso_option) + rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, + &skp->smk_host.sin_addr, &skp->smk_mask, + PF_INET, &audit_info); + else + rc = 0; skp->smk_label = sp; } /* * Now tell netlabel about the single label nature of * this host so that incoming packets get labeled. + * but only if we didn't get the special CIPSO option */ - - if (rc == 0) + if (rc == 0 && sp != smack_cipso_option) rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, smack_to_secid(skp->smk_label), &audit_info); -- cgit v1.2.3-59-g8ed1b From 06705bff9114531a997a7d0c2520bea0f2927410 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 28 Mar 2009 10:59:57 -0400 Subject: ext4: Regularize mount options Add support for using the mount options "barrier" and "nobarrier", and "auto_da_alloc" and "noauto_da_alloc", which is more consistent than "barrier=<0|1>" or "auto_da_alloc=<0|1>". Most other ext3/ext4 mount options use the foo/nofoo naming convention. We allow the old forms of these mount options for backwards compatibility. Signed-off-by: "Theodore Ts'o" --- Documentation/filesystems/ext4.txt | 25 +++++++++++++++++++++++-- fs/ext4/super.c | 30 ++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index 5c484aec2bab..97882df04865 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -183,8 +183,8 @@ commit=nrsec (*) Ext4 can be told to sync all its data and metadata performance. barrier=<0|1(*)> This enables/disables the use of write barriers in - the jbd code. barrier=0 disables, barrier=1 enables. - This also requires an IO stack which can support +barrier(*) the jbd code. barrier=0 disables, barrier=1 enables. +nobarrier This also requires an IO stack which can support barriers, and if jbd gets an error on a barrier write, it will disable again with a warning. Write barriers enforce proper on-disk ordering @@ -192,6 +192,9 @@ barrier=<0|1(*)> This enables/disables the use of write barriers in safe to use, at some performance penalty. If your disks are battery-backed in one way or another, disabling barriers may safely improve performance. + The mount options "barrier" and "nobarrier" can + also be used to enable or disable barriers, for + consistency with other ext4 mount options. inode_readahead=n This tuning parameter controls the maximum number of inode table blocks that ext4's inode @@ -313,6 +316,24 @@ journal_ioprio=prio The I/O priority (from 0 to 7, where 0 is the a slightly higher priority than the default I/O priority. +auto_da_alloc(*) Many broken applications don't use fsync() when +noauto_da_alloc replacing existing files via patterns such as + fd = open("foo.new")/write(fd,..)/close(fd)/ + rename("foo.new", "foo"), or worse yet, + fd = open("foo", O_TRUNC)/write(fd,..)/close(fd). + If auto_da_alloc is enabled, ext4 will detect + the replace-via-rename and replace-via-truncate + patterns and force that any delayed allocation + blocks are allocated such that at the next + journal commit, in the default data=ordered + mode, the data blocks of the new file are forced + to disk before the rename() operation is + commited. This provides roughly the same level + of guarantees as ext3, and avoids the + "zero-length" problem that can happen when a + system crashes before the delayed allocation + blocks are forced to disk. + Data Mode ========= There are 3 different data modes: diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 45d07cf9df34..9987bba99db3 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -867,7 +867,7 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) seq_puts(seq, ",data_err=abort"); if (test_opt(sb, NO_AUTO_DA_ALLOC)) - seq_puts(seq, ",auto_da_alloc=0"); + seq_puts(seq, ",noauto_da_alloc"); ext4_show_quota_options(seq, sb); return 0; @@ -1018,7 +1018,7 @@ enum { Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_nouid32, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, - Opt_auto_da_alloc, Opt_noload, Opt_nobh, Opt_bh, + Opt_auto_da_alloc, Opt_noauto_da_alloc, Opt_noload, Opt_nobh, Opt_bh, Opt_commit, Opt_min_batch_time, Opt_max_batch_time, Opt_journal_update, Opt_journal_dev, Opt_journal_checksum, Opt_journal_async_commit, @@ -1026,8 +1026,8 @@ enum { Opt_data_err_abort, Opt_data_err_ignore, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, - Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota, - Opt_grpquota, Opt_i_version, + Opt_ignore, Opt_barrier, Opt_nobarrier, Opt_err, Opt_resize, + Opt_usrquota, Opt_grpquota, Opt_i_version, Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_inode_readahead_blks, Opt_journal_ioprio }; @@ -1080,6 +1080,8 @@ static const match_table_t tokens = { {Opt_quota, "quota"}, {Opt_usrquota, "usrquota"}, {Opt_barrier, "barrier=%u"}, + {Opt_barrier, "barrier"}, + {Opt_nobarrier, "nobarrier"}, {Opt_i_version, "i_version"}, {Opt_stripe, "stripe=%u"}, {Opt_resize, "resize"}, @@ -1088,6 +1090,8 @@ static const match_table_t tokens = { {Opt_inode_readahead_blks, "inode_readahead_blks=%u"}, {Opt_journal_ioprio, "journal_ioprio=%u"}, {Opt_auto_da_alloc, "auto_da_alloc=%u"}, + {Opt_auto_da_alloc, "auto_da_alloc"}, + {Opt_noauto_da_alloc, "noauto_da_alloc"}, {Opt_err, NULL}, }; @@ -1422,9 +1426,14 @@ set_qf_format: case Opt_abort: set_opt(sbi->s_mount_opt, ABORT); break; + case Opt_nobarrier: + clear_opt(sbi->s_mount_opt, BARRIER); + break; case Opt_barrier: - if (match_int(&args[0], &option)) - return 0; + if (match_int(&args[0], &option)) { + set_opt(sbi->s_mount_opt, BARRIER); + break; + } if (option) set_opt(sbi->s_mount_opt, BARRIER); else @@ -1485,9 +1494,14 @@ set_qf_format: *journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, option); break; + case Opt_noauto_da_alloc: + set_opt(sbi->s_mount_opt,NO_AUTO_DA_ALLOC); + break; case Opt_auto_da_alloc: - if (match_int(&args[0], &option)) - return 0; + if (match_int(&args[0], &option)) { + clear_opt(sbi->s_mount_opt, NO_AUTO_DA_ALLOC); + break; + } if (option) clear_opt(sbi->s_mount_opt, NO_AUTO_DA_ALLOC); else -- cgit v1.2.3-59-g8ed1b From 764c16918fb2347b3cbc8f6030b2b6561911bc32 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 28 Mar 2009 21:34:40 +0100 Subject: i2c: Document the different ways to instantiate i2c devices On popular demand, here comes some documentation about how to instantiate i2c devices in the new (standard) i2c device driver binding model. I have also clarified how the class bitfield lets driver authors control which buses are probed in the auto-detect case, and warned more loudly against the abuse of this method. Signed-off-by: Jean Delvare Acked-by: Michael Lawnick Acked-by: Hans Verkuil --- Documentation/i2c/instantiating-devices | 167 ++++++++++++++++++++++++++++++++ Documentation/i2c/writing-clients | 19 +++- 2 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 Documentation/i2c/instantiating-devices (limited to 'Documentation') diff --git a/Documentation/i2c/instantiating-devices b/Documentation/i2c/instantiating-devices new file mode 100644 index 000000000000..b55ce57a84db --- /dev/null +++ b/Documentation/i2c/instantiating-devices @@ -0,0 +1,167 @@ +How to instantiate I2C devices +============================== + +Unlike PCI or USB devices, I2C devices are not enumerated at the hardware +level. Instead, the software must know which devices are connected on each +I2C bus segment, and what address these devices are using. For this +reason, the kernel code must instantiate I2C devices explicitly. There are +several ways to achieve this, depending on the context and requirements. + + +Method 1: Declare the I2C devices by bus number +----------------------------------------------- + +This method is appropriate when the I2C bus is a system bus as is the case +for many embedded systems. On such systems, each I2C bus has a number +which is known in advance. It is thus possible to pre-declare the I2C +devices which live on this bus. This is done with an array of struct +i2c_board_info which is registered by calling i2c_register_board_info(). + +Example (from omap2 h4): + +static struct i2c_board_info __initdata h4_i2c_board_info[] = { + { + I2C_BOARD_INFO("isp1301_omap", 0x2d), + .irq = OMAP_GPIO_IRQ(125), + }, + { /* EEPROM on mainboard */ + I2C_BOARD_INFO("24c01", 0x52), + .platform_data = &m24c01, + }, + { /* EEPROM on cpu card */ + I2C_BOARD_INFO("24c01", 0x57), + .platform_data = &m24c01, + }, +}; + +static void __init omap_h4_init(void) +{ + (...) + i2c_register_board_info(1, h4_i2c_board_info, + ARRAY_SIZE(h4_i2c_board_info)); + (...) +} + +The above code declares 3 devices on I2C bus 1, including their respective +addresses and custom data needed by their drivers. When the I2C bus in +question is registered, the I2C devices will be instantiated automatically +by i2c-core. + +The devices will be automatically unbound and destroyed when the I2C bus +they sit on goes away (if ever.) + + +Method 2: Instantiate the devices explicitly +-------------------------------------------- + +This method is appropriate when a larger device uses an I2C bus for +internal communication. A typical case is TV adapters. These can have a +tuner, a video decoder, an audio decoder, etc. usually connected to the +main chip by the means of an I2C bus. You won't know the number of the I2C +bus in advance, so the method 1 described above can't be used. Instead, +you can instantiate your I2C devices explicitly. This is done by filling +a struct i2c_board_info and calling i2c_new_device(). + +Example (from the sfe4001 network driver): + +static struct i2c_board_info sfe4001_hwmon_info = { + I2C_BOARD_INFO("max6647", 0x4e), +}; + +int sfe4001_init(struct efx_nic *efx) +{ + (...) + efx->board_info.hwmon_client = + i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info); + + (...) +} + +The above code instantiates 1 I2C device on the I2C bus which is on the +network adapter in question. + +A variant of this is when you don't know for sure if an I2C device is +present or not (for example for an optional feature which is not present +on cheap variants of a board but you have no way to tell them apart), or +it may have different addresses from one board to the next (manufacturer +changing its design without notice). In this case, you can call +i2c_new_probed_device() instead of i2c_new_device(). + +Example (from the pnx4008 OHCI driver): + +static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; + +static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) +{ + (...) + struct i2c_adapter *i2c_adap; + struct i2c_board_info i2c_info; + + (...) + i2c_adap = i2c_get_adapter(2); + memset(&i2c_info, 0, sizeof(struct i2c_board_info)); + strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE); + isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info, + normal_i2c); + i2c_put_adapter(i2c_adap); + (...) +} + +The above code instantiates up to 1 I2C device on the I2C bus which is on +the OHCI adapter in question. It first tries at address 0x2c, if nothing +is found there it tries address 0x2d, and if still nothing is found, it +simply gives up. + +The driver which instantiated the I2C device is responsible for destroying +it on cleanup. This is done by calling i2c_unregister_device() on the +pointer that was earlier returned by i2c_new_device() or +i2c_new_probed_device(). + + +Method 3: Probe an I2C bus for certain devices +---------------------------------------------- + +Sometimes you do not have enough information about an I2C device, not even +to call i2c_new_probed_device(). The typical case is hardware monitoring +chips on PC mainboards. There are several dozen models, which can live +at 25 different addresses. Given the huge number of mainboards out there, +it is next to impossible to build an exhaustive list of the hardware +monitoring chips being used. Fortunately, most of these chips have +manufacturer and device ID registers, so they can be identified by +probing. + +In that case, I2C devices are neither declared nor instantiated +explicitly. Instead, i2c-core will probe for such devices as soon as their +drivers are loaded, and if any is found, an I2C device will be +instantiated automatically. In order to prevent any misbehavior of this +mechanism, the following restrictions apply: +* The I2C device driver must implement the detect() method, which + identifies a supported device by reading from arbitrary registers. +* Only buses which are likely to have a supported device and agree to be + probed, will be probed. For example this avoids probing for hardware + monitoring chips on a TV adapter. + +Example: +See lm90_driver and lm90_detect() in drivers/hwmon/lm90.c + +I2C devices instantiated as a result of such a successful probe will be +destroyed automatically when the driver which detected them is removed, +or when the underlying I2C bus is itself destroyed, whichever happens +first. + +Those of you familiar with the i2c subsystem of 2.4 kernels and early 2.6 +kernels will find out that this method 3 is essentially similar to what +was done there. Two significant differences are: +* Probing is only one way to instantiate I2C devices now, while it was the + only way back then. Where possible, methods 1 and 2 should be preferred. + Method 3 should only be used when there is no other way, as it can have + undesirable side effects. +* I2C buses must now explicitly say which I2C driver classes can probe + them (by the means of the class bitfield), while all I2C buses were + probed by default back then. The default is an empty class which means + that no probing happens. The purpose of the class bitfield is to limit + the aforementioned undesirable side effects. + +Once again, method 3 should be avoided wherever possible. Explicit device +instantiation (methods 1 and 2) is much preferred for it is safer and +faster. diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients index 6b9af7d479c2..c1a06f989cf7 100644 --- a/Documentation/i2c/writing-clients +++ b/Documentation/i2c/writing-clients @@ -207,15 +207,26 @@ You simply have to define a detect callback which will attempt to identify supported devices (returning 0 for supported ones and -ENODEV for unsupported ones), a list of addresses to probe, and a device type (or class) so that only I2C buses which may have that type of device -connected (and not otherwise enumerated) will be probed. The i2c -core will then call you back as needed and will instantiate a device -for you for every successful detection. +connected (and not otherwise enumerated) will be probed. For example, +a driver for a hardware monitoring chip for which auto-detection is +needed would set its class to I2C_CLASS_HWMON, and only I2C adapters +with a class including I2C_CLASS_HWMON would be probed by this driver. +Note that the absence of matching classes does not prevent the use of +a device of that type on the given I2C adapter. All it prevents is +auto-detection; explicit instantiation of devices is still possible. Note that this mechanism is purely optional and not suitable for all devices. You need some reliable way to identify the supported devices (typically using device-specific, dedicated identification registers), otherwise misdetections are likely to occur and things can get wrong -quickly. +quickly. Keep in mind that the I2C protocol doesn't include any +standard way to detect the presence of a chip at a given address, let +alone a standard way to identify devices. Even worse is the lack of +semantics associated to bus transfers, which means that the same +transfer can be seen as a read operation by a chip and as a write +operation by another chip. For these reasons, explicit device +instantiation should always be preferred to auto-detection where +possible. Device Deletion -- cgit v1.2.3-59-g8ed1b From f02e3d74e9f89e3d49284e7c99217993b657f5b7 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 28 Mar 2009 21:34:40 +0100 Subject: i2c: Let checkpatch shout on users of the legacy model As suggested by Mauro Carvalho Chehab. Signed-off-by: Jean Delvare Cc: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 02ea3773535e..7907586c6e08 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -340,7 +340,8 @@ Who: Krzysztof Piotr Oledzki --------------------------- What: i2c_attach_client(), i2c_detach_client(), i2c_driver->detach_client() -When: 2.6.29 (ideally) or 2.6.30 (more likely) +When: 2.6.30 +Check: i2c_attach_client i2c_detach_client Why: Deprecated by the new (standard) device driver binding model. Use i2c_driver->probe() and ->remove() instead. Who: Jean Delvare -- cgit v1.2.3-59-g8ed1b From d2dd14ac1847082d4bb955619e86ed315c0ecd20 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 28 Mar 2009 21:34:41 +0100 Subject: i2c-nforce2: Add support for MCP67, MCP73, MCP78S and MCP79 The MCP78S and MCP79 appear to be compatible with the previous nForce chips as far as the SMBus controller is concerned. The MCP67 and MCP73 were not tested yet but I'd be very surprised if they weren't compatible too. Signed-off-by: Jean Delvare Cc: Oleg Ryjkov Cc: Malcolm Lalkaka Cc: Zbigniew Luszpinski --- Documentation/i2c/busses/i2c-nforce2 | 12 ++++++++---- drivers/i2c/busses/i2c-nforce2.c | 12 ++++++++++-- include/linux/pci_ids.h | 4 ++++ 3 files changed, 22 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/i2c/busses/i2c-nforce2 b/Documentation/i2c/busses/i2c-nforce2 index fae3495bcbaf..9698c396b830 100644 --- a/Documentation/i2c/busses/i2c-nforce2 +++ b/Documentation/i2c/busses/i2c-nforce2 @@ -7,10 +7,14 @@ Supported adapters: * nForce3 250Gb MCP 10de:00E4 * nForce4 MCP 10de:0052 * nForce4 MCP-04 10de:0034 - * nForce4 MCP51 10de:0264 - * nForce4 MCP55 10de:0368 - * nForce4 MCP61 10de:03EB - * nForce4 MCP65 10de:0446 + * nForce MCP51 10de:0264 + * nForce MCP55 10de:0368 + * nForce MCP61 10de:03EB + * nForce MCP65 10de:0446 + * nForce MCP67 10de:0542 + * nForce MCP73 10de:07D8 + * nForce MCP78S 10de:0752 + * nForce MCP79 10de:0AA2 Datasheet: not publicly available, but seems to be similar to the AMD-8111 SMBus 2.0 adapter. diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 05af6cd7f270..2ff4683703a8 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -31,10 +31,14 @@ nForce3 250Gb MCP 00E4 nForce4 MCP 0052 nForce4 MCP-04 0034 - nForce4 MCP51 0264 - nForce4 MCP55 0368 + nForce MCP51 0264 + nForce MCP55 0368 nForce MCP61 03EB nForce MCP65 0446 + nForce MCP67 0542 + nForce MCP73 07D8 + nForce MCP78S 0752 + nForce MCP79 0AA2 This driver supports the 2 SMBuses that are included in the MCP of the nForce2/3/4/5xx chipsets. @@ -315,6 +319,10 @@ static struct pci_device_id nforce2_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS) }, { 0 } }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 05dfa7c4fb64..5109fecde284 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1237,6 +1237,7 @@ #define PCI_DEVICE_ID_NVIDIA_NVENET_21 0x0451 #define PCI_DEVICE_ID_NVIDIA_NVENET_22 0x0452 #define PCI_DEVICE_ID_NVIDIA_NVENET_23 0x0453 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_SMBUS 0x0542 #define PCI_DEVICE_ID_NVIDIA_NVENET_24 0x054C #define PCI_DEVICE_ID_NVIDIA_NVENET_25 0x054D #define PCI_DEVICE_ID_NVIDIA_NVENET_26 0x054E @@ -1247,11 +1248,14 @@ #define PCI_DEVICE_ID_NVIDIA_NVENET_31 0x07DF #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE 0x0560 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE 0x056C +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS 0x0752 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759 #define PCI_DEVICE_ID_NVIDIA_NVENET_32 0x0760 #define PCI_DEVICE_ID_NVIDIA_NVENET_33 0x0761 #define PCI_DEVICE_ID_NVIDIA_NVENET_34 0x0762 #define PCI_DEVICE_ID_NVIDIA_NVENET_35 0x0763 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS 0x07D8 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS 0x0AA2 #define PCI_DEVICE_ID_NVIDIA_NVENET_36 0x0AB0 #define PCI_DEVICE_ID_NVIDIA_NVENET_37 0x0AB1 #define PCI_DEVICE_ID_NVIDIA_NVENET_38 0x0AB2 -- cgit v1.2.3-59-g8ed1b From 506a8b6c27cb08998dc13069fbdf6eb7ec748b99 Mon Sep 17 00:00:00 2001 From: Flavio Leitner Date: Sat, 28 Mar 2009 21:34:46 +0100 Subject: i2c-piix4: Add support for the Broadcom HT1100 chipset Add support for the Broadcom HT1100 LD chipset (SMBus function.) Signed-off-by: Flavio Leitner Signed-off-by: Jean Delvare --- Documentation/i2c/busses/i2c-piix4 | 2 +- drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-piix4.c | 4 +++- include/linux/pci_ids.h | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/i2c/busses/i2c-piix4 b/Documentation/i2c/busses/i2c-piix4 index ef1efa79b1df..f889481762b5 100644 --- a/Documentation/i2c/busses/i2c-piix4 +++ b/Documentation/i2c/busses/i2c-piix4 @@ -4,7 +4,7 @@ Supported adapters: * Intel 82371AB PIIX4 and PIIX4E * Intel 82443MX (440MX) Datasheet: Publicly available at the Intel website - * ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges + * ServerWorks OSB4, CSB5, CSB6, HT-1000 and HT-1100 southbridges Datasheet: Only available via NDA from ServerWorks * ATI IXP200, IXP300, IXP400, SB600, SB700 and SB800 southbridges Datasheet: Not publicly available diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 68650643d116..da809ad0996a 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -132,6 +132,7 @@ config I2C_PIIX4 Serverworks CSB5 Serverworks CSB6 Serverworks HT-1000 + Serverworks HT-1100 SMSC Victory66 This driver can also be built as a module. If so, the module diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 63d5e5978046..0249a7d762b9 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -20,7 +20,7 @@ /* Supports: Intel PIIX4, 440MX - Serverworks OSB4, CSB5, CSB6, HT-1000 + Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 ATI IXP200, IXP300, IXP400, SB600, SB700, SB800 SMSC Victory66 @@ -487,6 +487,8 @@ static struct pci_device_id piix4_ids[] = { PCI_DEVICE_ID_SERVERWORKS_CSB6) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_HT1100LD) }, { 0, } }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 5109fecde284..2c9e8080da5e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1479,6 +1479,7 @@ #define PCI_DEVICE_ID_SERVERWORKS_HT1000IDE 0x0214 #define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2 0x0217 #define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 +#define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408 #define PCI_VENDOR_ID_SBE 0x1176 #define PCI_DEVICE_ID_SBE_WANXL100 0x0301 -- cgit v1.2.3-59-g8ed1b From d1881d3192a3d3e8dc4f255b03187f4c36cb0617 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 30 Mar 2009 21:55:25 -0600 Subject: lguest: barrier me harder Impact: barrier correctness in example launcher I doubt either lguest user will complain about performance. Reported-by: Christoph Hellwig Cc: Jens Axboe Signed-off-by: Rusty Russell --- Documentation/lguest/lguest.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Documentation') diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index f2dbbf3bdeab..d36fcc0f2715 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -1630,6 +1630,13 @@ static bool service_io(struct device *dev) } } + /* OK, so we noted that it was pretty poor to use an fdatasync as a + * barrier. But Christoph Hellwig points out that we need a sync + * *afterwards* as well: "Barriers specify no reordering to the front + * or the back." And Jens Axboe confirmed it, so here we are: */ + if (out->type & VIRTIO_BLK_T_BARRIER) + fdatasync(vblk->fd); + /* We can't trigger an IRQ, because we're not the Launcher. It does * that when we tell it we're done. */ add_used(dev->vq, head, wlen); -- cgit v1.2.3-59-g8ed1b From afa5eb7c68689ced4284f01c96feed44a2d0a127 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 18 Mar 2009 09:13:37 +0100 Subject: HID: remove compat stuff This removal was scheduled and there is no problem with later distros to adapt for the new bus, thanks to aliases. module-init-tools map files are deprecated nowadays, so that the patch which introduced hid ones into the m-i-t won't be accepted and hence there is no reason for leaving compat stuff in. Signed-off-by: Jiri Slaby Cc: Jiri Kosina Signed-off-by: Jiri Kosina --- Documentation/feature-removal-schedule.txt | 7 --- drivers/hid/Kconfig | 12 ----- drivers/hid/Makefile | 4 -- drivers/hid/hid-a4tech.c | 2 - drivers/hid/hid-apple.c | 2 - drivers/hid/hid-belkin.c | 2 - drivers/hid/hid-cherry.c | 2 - drivers/hid/hid-chicony.c | 2 - drivers/hid/hid-core.c | 21 -------- drivers/hid/hid-cypress.c | 2 - drivers/hid/hid-drff.c | 2 - drivers/hid/hid-dummy.c | 87 ------------------------------ drivers/hid/hid-ezkey.c | 2 - drivers/hid/hid-gaff.c | 2 - drivers/hid/hid-gyration.c | 2 - drivers/hid/hid-kensington.c | 2 - drivers/hid/hid-kye.c | 2 - drivers/hid/hid-lg.c | 2 - drivers/hid/hid-microsoft.c | 2 - drivers/hid/hid-monterey.c | 2 - drivers/hid/hid-ntrig.c | 2 - drivers/hid/hid-petalynx.c | 2 - drivers/hid/hid-pl.c | 2 - drivers/hid/hid-samsung.c | 2 - drivers/hid/hid-sony.c | 2 - drivers/hid/hid-sunplus.c | 2 - drivers/hid/hid-tmff.c | 2 - drivers/hid/hid-topseed.c | 2 - drivers/hid/hid-zpff.c | 2 - include/linux/hid.h | 16 ------ 30 files changed, 195 deletions(-) delete mode 100644 drivers/hid/hid-dummy.c (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 1135996bec8b..fc5e85a5901c 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -273,13 +273,6 @@ Who: Glauber Costa --------------------------- -What: remove HID compat support -When: 2.6.29 -Why: needed only as a temporary solution until distros fix themselves up -Who: Jiri Slaby - ---------------------------- - What: print_fn_descriptor_symbol() When: October 2009 Why: The %pF vsprintf format provides the same functionality in a diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 88e16ef93247..63a2564f0f81 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -70,18 +70,6 @@ source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" depends on HID -config HID_COMPAT - bool "Load all HID drivers on hid core load" - default y - ---help--- - Compatible option for older userspace. If you have system without udev - support of module loading through aliases and also old - module-init-tools which can't handle hid bus, choose Y here. Otherwise - say N. If you say N and your userspace is old enough, the only - functionality you lose is modules autoloading. - - If unsure, say Y. - config HID_A4TECH tristate "A4 tech" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e6b72ed0d70a..1f7cb0fd4505 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,10 +8,6 @@ obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o hid-$(CONFIG_HIDRAW) += hidraw.o -ifdef CONFIG_HID_COMPAT -obj-m += hid-dummy.o -endif - hid-logitech-objs := hid-lg.o ifdef CONFIG_LOGITECH_FF hid-logitech-objs += hid-lgff.o diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index ebca00e6c103..42ea359e94cf 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -158,5 +158,3 @@ static void a4_exit(void) module_init(a4_init); module_exit(a4_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(a4tech); diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index cab3be7ef0ab..7359d9d88e46 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -474,5 +474,3 @@ static void apple_exit(void) module_init(apple_init); module_exit(apple_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(apple); diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c index 12c8a9ba6ed6..2f6723133a4b 100644 --- a/drivers/hid/hid-belkin.c +++ b/drivers/hid/hid-belkin.c @@ -101,5 +101,3 @@ static void belkin_exit(void) module_init(belkin_init); module_exit(belkin_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(belkin); diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c index b833b9769aba..ab8209e7e45c 100644 --- a/drivers/hid/hid-cherry.c +++ b/drivers/hid/hid-cherry.c @@ -83,5 +83,3 @@ static void ch_exit(void) module_init(ch_init); module_exit(ch_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(cherry); diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c index a54d4096e0f7..7f91076d8493 100644 --- a/drivers/hid/hid-chicony.c +++ b/drivers/hid/hid-chicony.c @@ -76,5 +76,3 @@ static void ch_exit(void) module_init(ch_init); module_exit(ch_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(chicony); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b96fbd5dab55..e56f8d5d3a50 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1819,15 +1819,6 @@ void hid_unregister_driver(struct hid_driver *hdrv) } EXPORT_SYMBOL_GPL(hid_unregister_driver); -#ifdef CONFIG_HID_COMPAT -static void hid_compat_load(struct work_struct *ws) -{ - request_module("hid-dummy"); -} -static DECLARE_WORK(hid_compat_work, hid_compat_load); -static struct workqueue_struct *hid_compat_wq; -#endif - static int __init hid_init(void) { int ret; @@ -1842,15 +1833,6 @@ static int __init hid_init(void) if (ret) goto err_bus; -#ifdef CONFIG_HID_COMPAT - hid_compat_wq = create_singlethread_workqueue("hid_compat"); - if (!hid_compat_wq) { - hidraw_exit(); - goto err; - } - queue_work(hid_compat_wq, &hid_compat_work); -#endif - return 0; err_bus: bus_unregister(&hid_bus_type); @@ -1860,9 +1842,6 @@ err: static void __exit hid_exit(void) { -#ifdef CONFIG_HID_COMPAT - destroy_workqueue(hid_compat_wq); -#endif hidraw_exit(); bus_unregister(&hid_bus_type); } diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 5d69d27b935d..9d6d3b91773b 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -154,5 +154,3 @@ static void cp_exit(void) module_init(cp_init); module_exit(cp_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(cypress); diff --git a/drivers/hid/hid-drff.c b/drivers/hid/hid-drff.c index 785d2492b5ef..34f3eb65100b 100644 --- a/drivers/hid/hid-drff.c +++ b/drivers/hid/hid-drff.c @@ -186,5 +186,3 @@ static void __exit dr_exit(void) module_init(dr_init); module_exit(dr_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(dragonrise); diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c deleted file mode 100644 index 74d765f38624..000000000000 --- a/drivers/hid/hid-dummy.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include - -static int __init hid_dummy_init(void) -{ -#ifdef CONFIG_HID_A4TECH_MODULE - HID_COMPAT_CALL_DRIVER(a4tech); -#endif -#ifdef CONFIG_HID_APPLE_MODULE - HID_COMPAT_CALL_DRIVER(apple); -#endif -#ifdef CONFIG_HID_BELKIN_MODULE - HID_COMPAT_CALL_DRIVER(belkin); -#endif -#ifdef CONFIG_HID_BRIGHT_MODULE - HID_COMPAT_CALL_DRIVER(bright); -#endif -#ifdef CONFIG_HID_CHERRY_MODULE - HID_COMPAT_CALL_DRIVER(cherry); -#endif -#ifdef CONFIG_HID_CHICONY_MODULE - HID_COMPAT_CALL_DRIVER(chicony); -#endif -#ifdef CONFIG_HID_CYPRESS_MODULE - HID_COMPAT_CALL_DRIVER(cypress); -#endif -#ifdef CONFIG_HID_DELL_MODULE - HID_COMPAT_CALL_DRIVER(dell); -#endif -#ifdef CONFIG_DRAGONRISE_FF_MODULE - HID_COMPAT_CALL_DRIVER(dragonrise); -#endif -#ifdef CONFIG_HID_EZKEY_MODULE - HID_COMPAT_CALL_DRIVER(ezkey); -#endif -#ifdef CONFIG_HID_KYE_MODULE - HID_COMPAT_CALL_DRIVER(kye); -#endif -#ifdef CONFIG_HID_GYRATION_MODULE - HID_COMPAT_CALL_DRIVER(gyration); -#endif -#ifdef CONFIG_HID_KENSINGTON_MODULE - HID_COMPAT_CALL_DRIVER(kensington); -#endif -#ifdef CONFIG_HID_LOGITECH_MODULE - HID_COMPAT_CALL_DRIVER(logitech); -#endif -#ifdef CONFIG_HID_MICROSOFT_MODULE - HID_COMPAT_CALL_DRIVER(microsoft); -#endif -#ifdef CONFIG_HID_MONTEREY_MODULE - HID_COMPAT_CALL_DRIVER(monterey); -#endif -#ifdef CONFIG_HID_NTRIG_MODULE - HID_COMPAT_CALL_DRIVER(ntrig); -#endif -#ifdef CONFIG_HID_PANTHERLORD_MODULE - HID_COMPAT_CALL_DRIVER(pantherlord); -#endif -#ifdef CONFIG_HID_PETALYNX_MODULE - HID_COMPAT_CALL_DRIVER(petalynx); -#endif -#ifdef CONFIG_HID_SAMSUNG_MODULE - HID_COMPAT_CALL_DRIVER(samsung); -#endif -#ifdef CONFIG_HID_SONY_MODULE - HID_COMPAT_CALL_DRIVER(sony); -#endif -#ifdef CONFIG_HID_SUNPLUS_MODULE - HID_COMPAT_CALL_DRIVER(sunplus); -#endif -#ifdef CONFIG_GREENASIA_FF_MODULE - HID_COMPAT_CALL_DRIVER(greenasia); -#endif -#ifdef CONFIG_THRUSTMASTER_FF_MODULE - HID_COMPAT_CALL_DRIVER(thrustmaster); -#endif -#ifdef CONFIG_ZEROPLUS_FF_MODULE - HID_COMPAT_CALL_DRIVER(zeroplus); -#endif - - return -EIO; -} -module_init(hid_dummy_init); - -MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c index deb42f931b7e..0a1fe054799b 100644 --- a/drivers/hid/hid-ezkey.c +++ b/drivers/hid/hid-ezkey.c @@ -91,5 +91,3 @@ static void ez_exit(void) module_init(ez_init); module_exit(ez_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(ezkey); diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c index 71211f6a4f02..510ad3ab8d33 100644 --- a/drivers/hid/hid-gaff.c +++ b/drivers/hid/hid-gaff.c @@ -181,5 +181,3 @@ static void __exit ga_exit(void) module_init(ga_init); module_exit(ga_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(greenasia); diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c index 04a0afec52ac..d42d222097a8 100644 --- a/drivers/hid/hid-gyration.c +++ b/drivers/hid/hid-gyration.c @@ -94,5 +94,3 @@ static void gyration_exit(void) module_init(gyration_init); module_exit(gyration_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(gyration); diff --git a/drivers/hid/hid-kensington.c b/drivers/hid/hid-kensington.c index 747fee5b2a73..7353bd79cbe9 100644 --- a/drivers/hid/hid-kensington.c +++ b/drivers/hid/hid-kensington.c @@ -61,5 +61,3 @@ static void ks_exit(void) module_init(ks_init); module_exit(ks_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(kensington); diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index ea7f412e31a9..72ee3fec56d9 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -67,5 +67,3 @@ static void kye_exit(void) module_init(kye_init); module_exit(kye_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(kye); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 83e07c9f4144..7b80cb694982 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -326,5 +326,3 @@ static void lg_exit(void) module_init(lg_init); module_exit(lg_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(logitech); diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 25b10dcad90d..5e9e37a0506d 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -210,5 +210,3 @@ static void ms_exit(void) module_init(ms_init); module_exit(ms_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(microsoft); diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c index f3a85a065f18..240f87618be6 100644 --- a/drivers/hid/hid-monterey.c +++ b/drivers/hid/hid-monterey.c @@ -78,5 +78,3 @@ static void mr_exit(void) module_init(mr_init); module_exit(mr_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(monterey); diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index db44fbd7bdf6..c5b252be9c21 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -78,5 +78,3 @@ static void ntrig_exit(void) module_init(ntrig_init); module_exit(ntrig_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(ntrig); diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c index 10945fe12d50..2e83e8ff891a 100644 --- a/drivers/hid/hid-petalynx.c +++ b/drivers/hid/hid-petalynx.c @@ -118,5 +118,3 @@ static void pl_exit(void) module_init(pl_init); module_exit(pl_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(petalynx); diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 9ad76bf71186..4db9a3483760 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -230,5 +230,3 @@ static void pl_exit(void) module_init(pl_init); module_exit(pl_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(pantherlord); diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index 15f3c0492450..07083aa6c19a 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c @@ -96,5 +96,3 @@ static void samsung_exit(void) module_init(samsung_init); module_exit(samsung_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(samsung); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index dd5a3979a4de..c2599388a350 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -148,5 +148,3 @@ static void sony_exit(void) module_init(sony_init); module_exit(sony_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(sony); diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c index 5ba68f7dbb78..e0a8fd36a85b 100644 --- a/drivers/hid/hid-sunplus.c +++ b/drivers/hid/hid-sunplus.c @@ -78,5 +78,3 @@ static void sp_exit(void) module_init(sp_init); module_exit(sp_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(sunplus); diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index 1b7cba0f7e1f..7c1f7b50330c 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -265,5 +265,3 @@ static void tm_exit(void) module_init(tm_init); module_exit(tm_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(thrustmaster); diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c index cca64a0564a9..152ccfabeba5 100644 --- a/drivers/hid/hid-topseed.c +++ b/drivers/hid/hid-topseed.c @@ -73,5 +73,3 @@ static void ts_exit(void) module_init(ts_init); module_exit(ts_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(topseed); diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index ea82f3718b21..85a198a18537 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -158,5 +158,3 @@ static void zp_exit(void) module_init(zp_init); module_exit(zp_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(zeroplus); diff --git a/include/linux/hid.h b/include/linux/hid.h index a46cda488695..a46cbea71d65 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -792,21 +792,5 @@ dbg_hid(const char *fmt, ...) __FILE__ , ## arg) #endif /* HID_FF */ -#ifdef __KERNEL__ -#ifdef CONFIG_HID_COMPAT -#define HID_COMPAT_LOAD_DRIVER(name) \ -/* prototype to avoid sparse warning */ \ -extern void hid_compat_##name(void); \ -void hid_compat_##name(void) { } \ -EXPORT_SYMBOL(hid_compat_##name) -#else -#define HID_COMPAT_LOAD_DRIVER(name) -#endif /* HID_COMPAT */ -#define HID_COMPAT_CALL_DRIVER(name) do { \ - extern void hid_compat_##name(void); \ - hid_compat_##name(); \ -} while (0) -#endif /* __KERNEL__ */ - #endif -- cgit v1.2.3-59-g8ed1b From e3375ac767b847127df57d186a26abf83d055982 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Sat, 10 Jan 2009 00:47:21 +0100 Subject: trivial: document ext3 semantics of 'ro' option a bit better ext3 has quite unexpected semantics or "ro" and defaults are not what they are documented to be, due to mkfs override. Signed-off-by: Pavel Machek Signed-off-by: "Theodore Ts'o" Signed-off-by: Jiri Kosina --- Documentation/filesystems/ext3.txt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ext3.txt b/Documentation/filesystems/ext3.txt index e5f3833a6ef8..570f9bd9be2b 100644 --- a/Documentation/filesystems/ext3.txt +++ b/Documentation/filesystems/ext3.txt @@ -14,6 +14,11 @@ Options When mounting an ext3 filesystem, the following option are accepted: (*) == default +ro Mount filesystem read only. Note that ext3 will replay + the journal (and thus write to the partition) even when + mounted "read only". Mount options "ro,noload" can be + used to prevent writes to the filesystem. + journal=update Update the ext3 file system's journal to the current format. @@ -27,7 +32,9 @@ journal_dev=devnum When the external journal device's major/minor numbers identified through its new major/minor numbers encoded in devnum. -noload Don't load the journal on mounting. +noload Don't load the journal on mounting. Note that this forces + mount of inconsistent filesystem, which can lead to + various problems. data=journal All data are committed into the journal prior to being written into the main file system. @@ -92,9 +99,12 @@ nocheck debug Extra debugging information is sent to syslog. -errors=remount-ro(*) Remount the filesystem read-only on an error. +errors=remount-ro Remount the filesystem read-only on an error. errors=continue Keep going on a filesystem error. errors=panic Panic and halt the machine if an error occurs. + (These mount options override the errors behavior + specified in the superblock, which can be + configured using tune2fs.) data_err=ignore(*) Just print an error message if an error occurs in a file data buffer in ordered mode. -- cgit v1.2.3-59-g8ed1b From caa790ba6cb88dccfab356960d93e2f4e0bd8704 Mon Sep 17 00:00:00 2001 From: Chris Samuel Date: Sat, 17 Jan 2009 00:01:18 +1100 Subject: trivial: cgroups: documentation typo and spelling corrections Minor typo and spelling corrections fixed whilst reading to learn about cgroups capabilities. Signed-off-by: Chris Samuel Acked-by: Paul Menage Signed-off-by: Jiri Kosina --- Documentation/cgroups/cgroups.txt | 10 +++++----- Documentation/cgroups/cpusets.txt | 12 ++++++------ Documentation/cgroups/devices.txt | 2 +- Documentation/cgroups/memory.txt | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index 93feb8444489..f4f5ee97d4db 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -56,7 +56,7 @@ hierarchy, and a set of subsystems; each subsystem has system-specific state attached to each cgroup in the hierarchy. Each hierarchy has an instance of the cgroup virtual filesystem associated with it. -At any one time there may be multiple active hierachies of task +At any one time there may be multiple active hierarchies of task cgroups. Each hierarchy is a partition of all tasks in the system. User level code may create and destroy cgroups by name in an @@ -124,10 +124,10 @@ following lines: / \ Prof (15%) students (5%) -Browsers like firefox/lynx go into the WWW network class, while (k)nfsd go +Browsers like Firefox/Lynx go into the WWW network class, while (k)nfsd go into NFS network class. -At the same time firefox/lynx will share an appropriate CPU/Memory class +At the same time Firefox/Lynx will share an appropriate CPU/Memory class depending on who launched it (prof/student). With the ability to classify tasks differently for different resources @@ -325,7 +325,7 @@ and then start a subshell 'sh' in that cgroup: Creating, modifying, using the cgroups can be done through the cgroup virtual filesystem. -To mount a cgroup hierarchy will all available subsystems, type: +To mount a cgroup hierarchy with all available subsystems, type: # mount -t cgroup xxx /dev/cgroup The "xxx" is not interpreted by the cgroup code, but will appear in @@ -521,7 +521,7 @@ always handled well. void post_clone(struct cgroup_subsys *ss, struct cgroup *cgrp) (cgroup_mutex held by caller) -Called at the end of cgroup_clone() to do any paramater +Called at the end of cgroup_clone() to do any parameter initialization which might be required before a task could attach. For example in cpusets, no task may attach before 'cpus' and 'mems' are set up. diff --git a/Documentation/cgroups/cpusets.txt b/Documentation/cgroups/cpusets.txt index 0611e9528c7c..f9ca389dddf4 100644 --- a/Documentation/cgroups/cpusets.txt +++ b/Documentation/cgroups/cpusets.txt @@ -131,7 +131,7 @@ Cpusets extends these two mechanisms as follows: - The hierarchy of cpusets can be mounted at /dev/cpuset, for browsing and manipulation from user space. - A cpuset may be marked exclusive, which ensures that no other - cpuset (except direct ancestors and descendents) may contain + cpuset (except direct ancestors and descendants) may contain any overlapping CPUs or Memory Nodes. - You can list all the tasks (by pid) attached to any cpuset. @@ -226,7 +226,7 @@ nodes with memory--using the cpuset_track_online_nodes() hook. -------------------------------- If a cpuset is cpu or mem exclusive, no other cpuset, other than -a direct ancestor or descendent, may share any of the same CPUs or +a direct ancestor or descendant, may share any of the same CPUs or Memory Nodes. A cpuset that is mem_exclusive *or* mem_hardwall is "hardwalled", @@ -427,7 +427,7 @@ child cpusets have this flag enabled. When doing this, you don't usually want to leave any unpinned tasks in the top cpuset that might use non-trivial amounts of CPU, as such tasks may be artificially constrained to some subset of CPUs, depending on -the particulars of this flag setting in descendent cpusets. Even if +the particulars of this flag setting in descendant cpusets. Even if such a task could use spare CPU cycles in some other CPUs, the kernel scheduler might not consider the possibility of load balancing that task to that underused CPU. @@ -531,9 +531,9 @@ be idle. Of course it takes some searching cost to find movable tasks and/or idle CPUs, the scheduler might not search all CPUs in the domain -everytime. In fact, in some architectures, the searching ranges on +every time. In fact, in some architectures, the searching ranges on events are limited in the same socket or node where the CPU locates, -while the load balance on tick searchs all. +while the load balance on tick searches all. For example, assume CPU Z is relatively far from CPU X. Even if CPU Z is idle while CPU X and the siblings are busy, scheduler can't migrate @@ -601,7 +601,7 @@ its new cpuset, then the task will continue to use whatever subset of MPOL_BIND nodes are still allowed in the new cpuset. If the task was using MPOL_BIND and now none of its MPOL_BIND nodes are allowed in the new cpuset, then the task will be essentially treated as if it -was MPOL_BIND bound to the new cpuset (even though its numa placement, +was MPOL_BIND bound to the new cpuset (even though its NUMA placement, as queried by get_mempolicy(), doesn't change). If a task is moved from one cpuset to another, then the kernel will adjust the tasks memory placement, as above, the next time that the kernel attempts diff --git a/Documentation/cgroups/devices.txt b/Documentation/cgroups/devices.txt index 7cc6e6a60672..57ca4c89fe5c 100644 --- a/Documentation/cgroups/devices.txt +++ b/Documentation/cgroups/devices.txt @@ -42,7 +42,7 @@ suffice, but we can decide the best way to adequately restrict movement as people get some experience with this. We may just want to require CAP_SYS_ADMIN, which at least is a separate bit from CAP_MKNOD. We may want to just refuse moving to a cgroup which -isn't a descendent of the current one. Or we may want to use +isn't a descendant of the current one. Or we may want to use CAP_MAC_ADMIN, since we really are trying to lock down root. CAP_SYS_ADMIN is needed to modify the whitelist or move another diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index e1501964df1e..a98a7fe7aabb 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt @@ -302,7 +302,7 @@ will be charged as a new owner of it. unevictable - # of pages cannot be reclaimed.(mlocked etc) Below is depend on CONFIG_DEBUG_VM. - inactive_ratio - VM inernal parameter. (see mm/page_alloc.c) + inactive_ratio - VM internal parameter. (see mm/page_alloc.c) recent_rotated_anon - VM internal parameter. (see mm/vmscan.c) recent_rotated_file - VM internal parameter. (see mm/vmscan.c) recent_scanned_anon - VM internal parameter. (see mm/vmscan.c) -- cgit v1.2.3-59-g8ed1b From 877d03105d04b2c13e241130277fa69c8d2564f0 Mon Sep 17 00:00:00 2001 From: Nick Andrew Date: Mon, 26 Jan 2009 11:06:57 +0100 Subject: trivial: Fix misspelling of firmware Fix misspelling of firmware. Signed-off-by: Nick Andrew Signed-off-by: Jiri Kosina --- Documentation/ia64/kvm.txt | 2 +- Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe/firmware.txt | 2 +- arch/mips/sgi-ip27/ip27-smp.c | 2 +- arch/sparc/kernel/head_64.S | 2 +- drivers/net/sb1250-mac.c | 2 +- drivers/net/tg3.c | 2 +- drivers/net/wireless/ipw2x00/ipw2100.c | 2 +- drivers/net/wireless/ipw2x00/ipw2200.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- drivers/net/wireless/libertas/cmd.c | 2 +- drivers/pci/pci.c | 2 +- drivers/platform/x86/thinkpad_acpi.c | 2 +- drivers/staging/otus/hal/hpmain.c | 2 +- drivers/usb/atm/ueagle-atm.c | 2 +- drivers/usb/serial/ChangeLog.history | 2 +- include/linux/libata.h | 2 +- kernel/power/disk.c | 4 ++-- sound/oss/pss.c | 2 +- sound/sh/aica.c | 2 +- 20 files changed, 21 insertions(+), 21 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ia64/kvm.txt b/Documentation/ia64/kvm.txt index 84f7cb3d5bec..ffb5c80bec3e 100644 --- a/Documentation/ia64/kvm.txt +++ b/Documentation/ia64/kvm.txt @@ -42,7 +42,7 @@ Note: For step 2, please make sure that host page size == TARGET_PAGE_SIZE of qe hg clone http://xenbits.xensource.com/ext/efi-vfirmware.hg you can get the firmware's binary in the directory of efi-vfirmware.hg/binaries. - (3) Rename the firware you owned to Flash.fd, and copy it to /usr/local/share/qemu + (3) Rename the firmware you owned to Flash.fd, and copy it to /usr/local/share/qemu 4. Boot up Linux or Windows guests: 4.1 Create or install a image for guest boot. If you have xen experience, it should be easy. diff --git a/Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe/firmware.txt b/Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe/firmware.txt index 6c238f59b2a9..249db3a15d15 100644 --- a/Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe/firmware.txt +++ b/Documentation/powerpc/dts-bindings/fsl/cpm_qe/qe/firmware.txt @@ -1,6 +1,6 @@ * Uploaded QE firmware - If a new firwmare has been uploaded to the QE (usually by the + If a new firmware has been uploaded to the QE (usually by the boot loader), then a 'firmware' child node should be added to the QE node. This node provides information on the uploaded firmware that device drivers may need. diff --git a/arch/mips/sgi-ip27/ip27-smp.c b/arch/mips/sgi-ip27/ip27-smp.c index 5b47d6b65275..cbcd7eb83bd1 100644 --- a/arch/mips/sgi-ip27/ip27-smp.c +++ b/arch/mips/sgi-ip27/ip27-smp.c @@ -221,7 +221,7 @@ static void __init ip27_smp_setup(void) * Assumption to be fixed: we're always booted on logical / physical * processor 0. While we're always running on logical processor 0 * this still means this is physical processor zero; it might for - * example be disabled in the firwware. + * example be disabled in the firmware. */ alloc_cpupda(0, 0); } diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S index a46c3a21e26d..3a1b7bf03cff 100644 --- a/arch/sparc/kernel/head_64.S +++ b/arch/sparc/kernel/head_64.S @@ -686,7 +686,7 @@ tlb_fixup_done: * point. * * There used to be enormous complexity wrt. transferring - * over from the firwmare's trap table to the Linux kernel's. + * over from the firmware's trap table to the Linux kernel's. * For example, there was a chicken & egg problem wrt. building * the OBP page tables, yet needing to be on the Linux kernel * trap table (to translate PAGE_OFFSET addresses) in order to diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 88dd2e09832f..ce7551e17ba7 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -2299,7 +2299,7 @@ static int sbmac_init(struct platform_device *pldev, long long base) eaddr = sc->sbm_hwaddr; /* - * Read the ethernet address. The firwmare left this programmed + * Read the ethernet address. The firmware left this programmed * for us in the ethernet address register for each mac. */ diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index f7efcecc4108..ed60b18addac 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -11225,7 +11225,7 @@ static int __devinit tg3_phy_probe(struct tg3 *tp) return tg3_phy_init(tp); /* Reading the PHY ID register can conflict with ASF - * firwmare access to the PHY hardware. + * firmware access to the PHY hardware. */ err = 0; if ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) || diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 115b70487502..f4e963ba768b 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -2362,7 +2362,7 @@ static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i) i * sizeof(struct ipw2100_status)); #ifdef IPW2100_DEBUG_C3 - /* Halt the fimrware so we can get a good image */ + /* Halt the firmware so we can get a good image */ write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_STOP_MASTER); j = 5; diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index b3449948a25a..f6174fdc12bf 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -8844,7 +8844,7 @@ static int ipw_wx_set_mode(struct net_device *dev, #endif /* CONFIG_IPW2200_MONITOR */ /* Free the existing firmware and reset the fw_loaded - * flag so ipw_load() will bring in the new firmawre */ + * flag so ipw_load() will bring in the new firmware */ free_firmware(); priv->ieee->iw_mode = wrqu->mode; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 663dc83be501..3889158b359c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1337,7 +1337,7 @@ static int iwl_read_ucode(struct iwl_priv *priv) /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely - * on the API version read from firware header from here on forward */ + * on the API version read from firmware header from here on forward */ if (api_ver < api_min || api_ver > api_max) { IWL_ERR(priv, "Driver unable to support your firmware API. " diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index a71b08ca7c71..9d5f97dd7c73 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2562,7 +2562,7 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely - * on the API version read from firware header from here on forward */ + * on the API version read from firmware header from here on forward */ if (api_ver < api_min || api_ver > api_max) { IWL_ERR(priv, "Driver unable to support your firmware API. " diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 639dd02d3d31..8c3605cdc64c 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -1649,7 +1649,7 @@ static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv) /** * @brief This function executes next command in command - * pending queue. It will put fimware back to PS mode + * pending queue. It will put firmware back to PS mode * if applicable. * * @param priv A pointer to struct lbs_private structure diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6d6120007af4..dab33a21d49a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -550,7 +550,7 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state) * @dev: PCI device to handle. * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. * - * Transition a device to a new power state, using the platform formware and/or + * Transition a device to a new power state, using the platform firmware and/or * the device's PCI PM registers. * * RETURN VALUE: diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d2433204a40c..814cb6520673 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -5811,7 +5811,7 @@ static struct ibm_struct volume_driver_data = { * ThinkPads from this same time period (and earlier) probably lack the * tachometer as well. * - * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare + * Unfortunately a lot of ThinkPads with new-style ECs but whose firmware * was never fixed by IBM to report the EC firmware version string * probably support the tachometer (like the early X models), so * detecting it is quite hard. We need more data to know for sure. diff --git a/drivers/staging/otus/hal/hpmain.c b/drivers/staging/otus/hal/hpmain.c index 2e65c466aae8..dab278326931 100644 --- a/drivers/staging/otus/hal/hpmain.c +++ b/drivers/staging/otus/hal/hpmain.c @@ -152,7 +152,7 @@ u16_t zfHpInit(zdev_t* dev, u32_t frequency) else { #ifndef ZM_OTUS_LINUX_PHASE_2 - /* donwload the normal frimware */ + /* download the normal firmware */ if ((ret = zfFirmwareDownload(dev, (u32_t*)zcFwImage, (u32_t)zcFwImageSize, ZM_FIRMWARE_WLAN_ADDR)) != ZM_SUCCESS) { diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index b6483dd98acc..9cf9ff69e3e3 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -626,7 +626,7 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *conte goto err_fw_corrupted; /* - * Start to upload formware : send reset + * Start to upload firmware : send reset */ value = 1; ret = uea_send_modem_cmd(usb, F8051_USBCS, sizeof(value), &value); diff --git a/drivers/usb/serial/ChangeLog.history b/drivers/usb/serial/ChangeLog.history index c1b279939bbf..f13fd488ebec 100644 --- a/drivers/usb/serial/ChangeLog.history +++ b/drivers/usb/serial/ChangeLog.history @@ -715,7 +715,7 @@ io_edgeport.c Change Log comments: 0.2 (01/30/2000) greg kroah-hartman Milestone 1 release. - Device is found by USB subsystem, enumerated, fimware is downloaded + Device is found by USB subsystem, enumerated, firmware is downloaded and the descriptors are printed to the debug log, config is set, and green light starts to blink. Open port works, and data can be sent and received at the default settings of the UART. Loopback connector diff --git a/include/linux/libata.h b/include/linux/libata.h index 76262d83656b..b450a2628855 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -379,7 +379,7 @@ enum { ATA_HORKAGE_BRIDGE_OK = (1 << 10), /* no bridge limits */ ATA_HORKAGE_ATAPI_MOD16_DMA = (1 << 11), /* use ATAPI DMA for commands not multiple of 16 bytes */ - ATA_HORKAGE_FIRMWARE_WARN = (1 << 12), /* firwmare update warning */ + ATA_HORKAGE_FIRMWARE_WARN = (1 << 12), /* firmware update warning */ ATA_HORKAGE_1_5_GBPS = (1 << 13), /* force 1.5 Gbps */ /* DMA mask for user DMA control: User visible values; DO NOT diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 4a4a206b1979..9d1c1a0de350 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -265,7 +265,7 @@ static int create_image(int platform_mode) * hibernation_snapshot - quiesce devices and create the hibernation * snapshot image. * @platform_mode - if set, use the platform driver, if available, to - * prepare the platform frimware for the power transition. + * prepare the platform firmware for the power transition. * * Must be called with pm_mutex held */ @@ -378,7 +378,7 @@ static int resume_target_kernel(void) * hibernation_restore - quiesce devices and restore the hibernation * snapshot image. If successful, control returns in hibernation_snaphot() * @platform_mode - if set, use the platform driver, if available, to - * prepare the platform frimware for the transition. + * prepare the platform firmware for the transition. * * Must be called with pm_mutex held */ diff --git a/sound/oss/pss.c b/sound/oss/pss.c index 16517a5a1301..83f5ee236b12 100644 --- a/sound/oss/pss.c +++ b/sound/oss/pss.c @@ -46,7 +46,7 @@ * load the driver as it did in previous versions. * 04-07-1999: Anthony Barbachan * Added module parameter pss_firmware to allow the user to tell - * the driver where the fireware file is located. The default + * the driver where the firmware file is located. The default * setting is the previous hardcoded setting "/etc/sound/pss_synth". * 00-03-03: Christoph Hellwig * Adapted to module_init/module_exit diff --git a/sound/sh/aica.c b/sound/sh/aica.c index f551233c5a08..583a3693df75 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -565,7 +565,7 @@ static int load_aica_firmware(void) err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev); if (unlikely(err)) return err; - /* write firware into memory */ + /* write firmware into memory */ spu_disable(); spu_memload(0, fw_entry->data, fw_entry->size); spu_enable(); -- cgit v1.2.3-59-g8ed1b From 6d5e147dd034d9ceedc89fe39f4284700944f0c8 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 3 Feb 2009 11:57:13 +0100 Subject: trivial: Give the right path in Documentation example While the Documentation example creates /cgroup/test, it removes /test/cgroup, which is clearly not the intended path. Change that to /cgroup/test. Acked-by: KAMEZAWA Hiroyuki Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Jiri Kosina --- Documentation/cgroups/memcg_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/memcg_test.txt b/Documentation/cgroups/memcg_test.txt index 523a9c16c400..a9263596f8d8 100644 --- a/Documentation/cgroups/memcg_test.txt +++ b/Documentation/cgroups/memcg_test.txt @@ -356,7 +356,7 @@ Under below explanation, we assume CONFIG_MEM_RES_CTRL_SWAP=y. (Shell-B) # move all tasks in /cgroup/test to /cgroup # /sbin/swapoff -a - # rmdir /test/cgroup + # rmdir /cgroup/test # kill malloc task. Of course, tmpfs v.s. swapoff test should be tested, too. -- cgit v1.2.3-59-g8ed1b From 21acb9caa2e30b100e9a1943d995bb99d40f4035 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 4 Feb 2009 10:12:08 +0100 Subject: trivial: fix where cgroup documentation is not correctly referred to cgroup documentation was moved to Documentation/cgroups/. There are some places that still refer to Documentation/controllers/, Documentation/cgroups.txt and Documentation/cpusets.txt. Fix those. Signed-off-by: Thadeu Lima de Souza Cascardo Reviewed-by: Li Zefan Acked-by: Paul Menage Signed-off-by: Jiri Kosina --- Documentation/00-INDEX | 4 ++-- Documentation/cgroups/00-INDEX | 18 ++++++++++++++++++ Documentation/kernel-parameters.txt | 4 ++-- Documentation/scheduler/sched-rt-group.txt | 2 +- Documentation/vm/numa_memory_policy.txt | 3 ++- Documentation/vm/page_migration | 3 ++- Documentation/x86/x86_64/fake-numa-for-cpusets | 5 +++-- include/linux/cgroup.h | 5 ++++- init/Kconfig | 2 +- 9 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 Documentation/cgroups/00-INDEX (limited to 'Documentation') diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 2a39aeba1464..d05737aaa84b 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -86,6 +86,8 @@ cachetlb.txt - describes the cache/TLB flushing interfaces Linux uses. cdrom/ - directory with information on the CD-ROM drivers that Linux has. +cgroups/ + - cgroups features, including cpusets and memory controller. connector/ - docs on the netlink based userspace<->kernel space communication mod. console/ @@ -98,8 +100,6 @@ cpu-load.txt - document describing how CPU load statistics are collected. cpuidle/ - info on CPU_IDLE, CPU idle state management subsystem. -cpusets.txt - - documents the cpusets feature; assign CPUs and Mem to a set of tasks. cputopology.txt - documentation on how CPU topology info is exported via sysfs. cris/ diff --git a/Documentation/cgroups/00-INDEX b/Documentation/cgroups/00-INDEX new file mode 100644 index 000000000000..3f58fa3d6d00 --- /dev/null +++ b/Documentation/cgroups/00-INDEX @@ -0,0 +1,18 @@ +00-INDEX + - this file +cgroups.txt + - Control Groups definition, implementation details, examples and API. +cpuacct.txt + - CPU Accounting Controller; account CPU usage for groups of tasks. +cpusets.txt + - documents the cpusets feature; assign CPUs and Mem to a set of tasks. +devices.txt + - Device Whitelist Controller; description, interface and security. +freezer-subsystem.txt + - checkpointing; rationale to not use signals, interface. +memcg_test.txt + - Memory Resource Controller; implementation details. +memory.txt + - Memory Resource Controller; design, accounting, interface, testing. +resource_counter.txt + - Resource Counter API. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index be3bde51b564..755def2cb071 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1593,7 +1593,7 @@ and is between 256 and 4096 characters. It is defined in the file nosoftlockup [KNL] Disable the soft-lockup detector. noswapaccount [KNL] Disable accounting of swap in memory resource - controller. (See Documentation/controllers/memory.txt) + controller. (See Documentation/cgroups/memory.txt) nosync [HW,M68K] Disables sync negotiation for all devices. @@ -1932,7 +1932,7 @@ and is between 256 and 4096 characters. It is defined in the file relax_domain_level= [KNL, SMP] Set scheduler's default relax_domain_level. - See Documentation/cpusets.txt. + See Documentation/cgroups/cpusets.txt. reserve= [KNL,BUGS] Force the kernel to ignore some iomem area diff --git a/Documentation/scheduler/sched-rt-group.txt b/Documentation/scheduler/sched-rt-group.txt index 3ef339f491e0..5ba4d3fc625a 100644 --- a/Documentation/scheduler/sched-rt-group.txt +++ b/Documentation/scheduler/sched-rt-group.txt @@ -126,7 +126,7 @@ This uses the /cgroup virtual file system and "/cgroup//cpu.rt_runtime_u to control the CPU time reserved for each control group instead. For more information on working with control groups, you should read -Documentation/cgroups.txt as well. +Documentation/cgroups/cgroups.txt as well. Group settings are checked against the following limits in order to keep the configuration schedulable: diff --git a/Documentation/vm/numa_memory_policy.txt b/Documentation/vm/numa_memory_policy.txt index 6aaaeb38730c..be45dbb9d7f2 100644 --- a/Documentation/vm/numa_memory_policy.txt +++ b/Documentation/vm/numa_memory_policy.txt @@ -8,7 +8,8 @@ The current memory policy support was added to Linux 2.6 around May 2004. This document attempts to describe the concepts and APIs of the 2.6 memory policy support. -Memory policies should not be confused with cpusets (Documentation/cpusets.txt) +Memory policies should not be confused with cpusets +(Documentation/cgroups/cpusets.txt) which is an administrative mechanism for restricting the nodes from which memory may be allocated by a set of processes. Memory policies are a programming interface that a NUMA-aware application can take advantage of. When diff --git a/Documentation/vm/page_migration b/Documentation/vm/page_migration index d5fdfd34bbaf..6513fe2d90b8 100644 --- a/Documentation/vm/page_migration +++ b/Documentation/vm/page_migration @@ -37,7 +37,8 @@ locations. Larger installations usually partition the system using cpusets into sections of nodes. Paul Jackson has equipped cpusets with the ability to -move pages when a task is moved to another cpuset (See ../cpusets.txt). +move pages when a task is moved to another cpuset (See +Documentation/cgroups/cpusets.txt). Cpusets allows the automation of process locality. If a task is moved to a new cpuset then also all its pages are moved with it so that the performance of the process does not sink dramatically. Also the pages diff --git a/Documentation/x86/x86_64/fake-numa-for-cpusets b/Documentation/x86/x86_64/fake-numa-for-cpusets index 33bb56655991..0f11d9becb0b 100644 --- a/Documentation/x86/x86_64/fake-numa-for-cpusets +++ b/Documentation/x86/x86_64/fake-numa-for-cpusets @@ -7,7 +7,8 @@ you can create fake NUMA nodes that represent contiguous chunks of memory and assign them to cpusets and their attached tasks. This is a way of limiting the amount of system memory that are available to a certain class of tasks. -For more information on the features of cpusets, see Documentation/cpusets.txt. +For more information on the features of cpusets, see +Documentation/cgroups/cpusets.txt. There are a number of different configurations you can use for your needs. For more information on the numa=fake command line option and its various ways of configuring fake nodes, see Documentation/x86/x86_64/boot-options.txt. @@ -32,7 +33,7 @@ A machine may be split as follows with "numa=fake=4*512," as reported by dmesg: On node 3 totalpages: 131072 Now following the instructions for mounting the cpusets filesystem from -Documentation/cpusets.txt, you can assign fake nodes (i.e. contiguous memory +Documentation/cgroups/cpusets.txt, you can assign fake nodes (i.e. contiguous memory address spaces) to individual cpusets: [root@xroads /]# mkdir exampleset diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 499900d0cee7..b837631fe499 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -342,7 +342,10 @@ int cgroup_task_count(const struct cgroup *cgrp); /* Return true if the cgroup is a descendant of the current cgroup */ int cgroup_is_descendant(const struct cgroup *cgrp); -/* Control Group subsystem type. See Documentation/cgroups.txt for details */ +/* + * Control Group subsystem type. + * See Documentation/cgroups/cgroups.txt for details + */ struct cgroup_subsys { struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss, diff --git a/init/Kconfig b/init/Kconfig index bcffc0e47647..99eb4196bd0a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -565,7 +565,7 @@ config CGROUP_MEM_RES_CTLR select MM_OWNER help Provides a memory resource controller that manages both anonymous - memory and page cache. (See Documentation/controllers/memory.txt) + memory and page cache. (See Documentation/cgroups/memory.txt) Note that setting this option increases fixed memory overhead associated with each page of memory in the system. By this, -- cgit v1.2.3-59-g8ed1b From f5360bdc5539ccd7644df7acf27e8c740ba8cf6e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Jan 2009 06:09:05 -0300 Subject: V4L/DVB (10252): v4l2 doc: explain why v4l2_device_unregister_subdev() has to be called. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index ff124374e9ba..cc350624237d 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -340,6 +340,12 @@ Make sure to call v4l2_device_unregister_subdev(sd) when the remove() callback is called. This will unregister the sub-device from the bridge driver. It is safe to call this even if the sub-device was never registered. +You need to do this because when the bridge driver destroys the i2c adapter +the remove() callbacks are called of the i2c devices on that adapter. +After that the corresponding v4l2_subdev structures are invalid, so they +have to be unregistered first. Calling v4l2_device_unregister_subdev(sd) +from the remove() callback ensures that this is always done correctly. + The bridge driver also has some helper functions it can use: -- cgit v1.2.3-59-g8ed1b From c76b638ca20d6cbf91ee017c6f2afd7d3fcd57ff Mon Sep 17 00:00:00 2001 From: Antoine Jacquet Date: Sat, 17 Jan 2009 22:49:08 -0300 Subject: V4L/DVB (10263): zr364xx: add support for Aiptek DV T300 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Hámorszky Balázs Signed-off-by: Antoine Jacquet Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/zr364xx.txt | 1 + drivers/media/video/zr364xx.c | 1 + 2 files changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/zr364xx.txt b/Documentation/video4linux/zr364xx.txt index 5c81e3ae6458..7f3d1955d214 100644 --- a/Documentation/video4linux/zr364xx.txt +++ b/Documentation/video4linux/zr364xx.txt @@ -65,3 +65,4 @@ Vendor Product Distributor Model 0x06d6 0x003b Trust Powerc@m 970Z 0x0a17 0x004e Pentax Optio 50 0x041e 0x405d Creative DiVi CAM 516 +0x08ca 0x2102 Aiptek DV T300 diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 93023560f324..f2f8cdd71c46 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -96,6 +96,7 @@ static struct usb_device_id device_table[] = { {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, + {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 }, {} /* Terminating entry */ }; -- cgit v1.2.3-59-g8ed1b From 96318d0cca02a91b22a2e1a1097ffeea0b3becae Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sat, 17 Jan 2009 12:11:20 -0300 Subject: V4L/DVB (10266): Add support for TurboSight TBS6920 DVB-S2 PCI-e card. TurboSight TBS6920 DVB-S2 PCI-e card contains cx23885 PCI-e bridge and cx24116 demodulator. http://www.linuxtv.org/wiki/index.php/TBS_6920 The card tested by me (Igor). Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx23885 | 1 + drivers/media/video/cx23885/cx23885-cards.c | 18 ++++++++++++++++++ drivers/media/video/cx23885/cx23885-dvb.c | 29 +++++++++++++++++++++++++++++ drivers/media/video/cx23885/cx23885.h | 1 + 4 files changed, 49 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 35ea130e9898..3af13a06ee6e 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -12,3 +12,4 @@ 11 -> DViCO FusionHDTV DVB-T Dual Express [18ac:db78] 12 -> Leadtek Winfast PxDVR3200 H [107d:6681] 13 -> Compro VideoMate E650F [185b:e800] + 14 -> TurboSight TBS 6920 [6920:8888] diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index caa098beeecf..0b050bc88ef5 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -162,6 +162,10 @@ struct cx23885_board cx23885_boards[] = { .name = "Compro VideoMate E650F", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_TBS_6920] = { + .name = "TurboSight TBS 6920", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -245,6 +249,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x185b, .subdevice = 0xe800, .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F, + }, { + .subvendor = 0x6920, + .subdevice = 0x8888, + .card = CX23885_BOARD_TBS_6920, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -552,6 +560,11 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) mdelay(20); cx_set(GP0_IO, 0x00040004); break; + case CX23885_BOARD_TBS_6920: + cx_write(MC417_CTL, 0x00000036); + cx_write(MC417_OEN, 0x00001000); + cx_write(MC417_RWD, 0x00001800); + break; } } @@ -632,6 +645,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_TBS_6920: + ts1->gen_ctrl_val = 0x5; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 1c454128a9df..3e0b04074e55 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -43,6 +43,7 @@ #include "dib7000p.h" #include "dibx000_common.h" #include "zl10353.h" +#include "cx24116.h" static unsigned int debug; @@ -308,6 +309,24 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = { .no_tuner = 1, }; +static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct cx23885_tsport *port = fe->dvb->priv; + struct cx23885_dev *dev = port->dev; + + if (voltage == SEC_VOLTAGE_18) + cx_write(MC417_RWD, 0x00001e00);/* GPIO-13 high */ + else if (voltage == SEC_VOLTAGE_13) + cx_write(MC417_RWD, 0x00001a00);/* GPIO-13 low */ + else + cx_write(MC417_RWD, 0x00001800);/* GPIO-12 low */ + return 0; +} + +static struct cx24116_config tbs_cx24116_config = { + .demod_address = 0x05, +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -525,6 +544,16 @@ static int dvb_register(struct cx23885_tsport *port) if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) fe->ops.tuner_ops.set_config(fe, &ctl); } + break; + case CX23885_BOARD_TBS_6920: + i2c_bus = &dev->i2c_bus[0]; + + fe0->dvb.frontend = dvb_attach(cx24116_attach, + &tbs_cx24116_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) + fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 67828029fc69..b4f23238598f 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -67,6 +67,7 @@ #define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11 #define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12 #define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13 +#define CX23885_BOARD_TBS_6920 14 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ -- cgit v1.2.3-59-g8ed1b From 579943f5487baa7f9fd8e3189a4f357d6b06c76d Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sat, 17 Jan 2009 12:18:26 -0300 Subject: V4L/DVB (10267): Add support for TeVii S470 DVB-S2 PCI-e card. TeVii S470 DVB-S2 PCI-e card contains cx23885 PCI-e bridge and cx24116 demodulator. http://www.linuxtv.org/wiki/index.php/TeVii_S470 The card tested by me (Igor). Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx23885 | 1 + drivers/media/video/cx23885/cx23885-cards.c | 10 ++++++++++ drivers/media/video/cx23885/cx23885-dvb.c | 14 ++++++++++++++ drivers/media/video/cx23885/cx23885.h | 1 + 4 files changed, 26 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 3af13a06ee6e..43d290ea75bf 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -13,3 +13,4 @@ 12 -> Leadtek Winfast PxDVR3200 H [107d:6681] 13 -> Compro VideoMate E650F [185b:e800] 14 -> TurboSight TBS 6920 [6920:8888] + 15 -> TeVii S470 [d470:9022] diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 0b050bc88ef5..dbc59d26f6f6 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -166,6 +166,10 @@ struct cx23885_board cx23885_boards[] = { .name = "TurboSight TBS 6920", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_TEVII_S470] = { + .name = "TeVii S470", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -253,6 +257,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x6920, .subdevice = 0x8888, .card = CX23885_BOARD_TBS_6920, + }, { + .subvendor = 0xd470, + .subdevice = 0x9022, + .card = CX23885_BOARD_TEVII_S470, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -561,6 +569,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); break; case CX23885_BOARD_TBS_6920: + case CX23885_BOARD_TEVII_S470: cx_write(MC417_CTL, 0x00000036); cx_write(MC417_OEN, 0x00001000); cx_write(MC417_RWD, 0x00001800); @@ -645,6 +654,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_TBS_6920: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 3e0b04074e55..a6b62a7bf618 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -327,6 +327,10 @@ static struct cx24116_config tbs_cx24116_config = { .demod_address = 0x05, }; +static struct cx24116_config tevii_cx24116_config = { + .demod_address = 0x55, +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -554,6 +558,16 @@ static int dvb_register(struct cx23885_tsport *port) if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + break; + case CX23885_BOARD_TEVII_S470: + i2c_bus = &dev->i2c_bus[1]; + + fe0->dvb.frontend = dvb_attach(cx24116_attach, + &tevii_cx24116_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) + fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index b4f23238598f..01856fb48a40 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -68,6 +68,7 @@ #define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12 #define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13 #define CX23885_BOARD_TBS_6920 14 +#define CX23885_BOARD_TEVII_S470 15 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ -- cgit v1.2.3-59-g8ed1b From c9b8b04b267f9a7e472daa06cdf6d4963d503d1f Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sat, 17 Jan 2009 12:23:31 -0300 Subject: V4L/DVB (10269): Add support for DVBWorld DVBS2 PCI-e 2005. DVBWorld DVBS2 PCI-e 2005 card contains cx23885 PCI-e bridge and cx24116 demodulator. http://www.linuxtv.org/wiki/index.php/DVBWorld_DVB-S2_2005_PCI-Express_Card The card tested by me (Igor). Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx23885 | 1 + drivers/media/video/cx23885/cx23885-cards.c | 9 +++++++++ drivers/media/video/cx23885/cx23885-dvb.c | 11 +++++++++++ drivers/media/video/cx23885/cx23885.h | 1 + 4 files changed, 22 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 43d290ea75bf..5937ff958f04 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -14,3 +14,4 @@ 13 -> Compro VideoMate E650F [185b:e800] 14 -> TurboSight TBS 6920 [6920:8888] 15 -> TeVii S470 [d470:9022] + 16 -> DVBWorld DVB-S2 2005 [0001:2005] diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index dbc59d26f6f6..7ff339a2e3f2 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -170,6 +170,10 @@ struct cx23885_board cx23885_boards[] = { .name = "TeVii S470", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_DVBWORLD_2005] = { + .name = "DVBWorld DVB-S2 2005", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -261,6 +265,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0xd470, .subdevice = 0x9022, .card = CX23885_BOARD_TEVII_S470, + }, { + .subvendor = 0x0001, + .subdevice = 0x2005, + .card = CX23885_BOARD_DVBWORLD_2005, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -656,6 +664,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_TBS_6920: + case CX23885_BOARD_DVBWORLD_2005: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index a6b62a7bf618..14a6540b826c 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -331,6 +331,10 @@ static struct cx24116_config tevii_cx24116_config = { .demod_address = 0x55, }; +static struct cx24116_config dvbworld_cx24116_config = { + .demod_address = 0x05, +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -569,6 +573,13 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; break; + case CX23885_BOARD_DVBWORLD_2005: + i2c_bus = &dev->i2c_bus[1]; + + fe0->dvb.frontend = dvb_attach(cx24116_attach, + &dvbworld_cx24116_config, + &i2c_bus->i2c_adap); + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 01856fb48a40..37a88b1683c3 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -69,6 +69,7 @@ #define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13 #define CX23885_BOARD_TBS_6920 14 #define CX23885_BOARD_TEVII_S470 15 +#define CX23885_BOARD_DVBWORLD_2005 16 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ -- cgit v1.2.3-59-g8ed1b From ade0815c16734e8c25dbac9faf5b5d63bcccd533 Mon Sep 17 00:00:00 2001 From: Douglas Kosovic Date: Thu, 22 Jan 2009 23:07:26 -0300 Subject: V4L/DVB (10299): bttv: Add support for IVCE-8784 support for V4L2 bttv driver It's a quad Bt878 PCI-e x1 capture board that's basically the same as the IVC-200 (quad Bt878 PCI) capture board that's currently supported in the V4L2 bttv driver. Manufacturer's web page for IVCE-8784 with photo and info: http://www.iei.com.tw/en/product_IPC.asp?model=IVCE-8784 Signed-off-by: Douglas Kosovic Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.bttv | 1 + drivers/media/video/bt8xx/bttv-cards.c | 17 +++++++++++++++++ drivers/media/video/bt8xx/bttv.h | 1 + 3 files changed, 19 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index 0d93fa1ac25e..4dfe62641374 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -154,3 +154,4 @@ 153 -> PHYTEC VD-012 (bt878) 154 -> PHYTEC VD-012-X1 (bt878) 155 -> PHYTEC VD-012-X2 (bt878) +156 -> IVCE-8784 [0000:f050,0001:f050,0002:f050,0003:f050] diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index d24dcc025e37..9dfd8c70e4fb 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -246,6 +246,10 @@ static struct CARD { { 0xa182ff0d, BTTV_BOARD_IVC120, "IVC-120G" }, { 0xa182ff0e, BTTV_BOARD_IVC120, "IVC-120G" }, { 0xa182ff0f, BTTV_BOARD_IVC120, "IVC-120G" }, + { 0xf0500000, BTTV_BOARD_IVCE8784, "IVCE-8784" }, + { 0xf0500001, BTTV_BOARD_IVCE8784, "IVCE-8784" }, + { 0xf0500002, BTTV_BOARD_IVCE8784, "IVCE-8784" }, + { 0xf0500003, BTTV_BOARD_IVCE8784, "IVCE-8784" }, { 0x41424344, BTTV_BOARD_GRANDTEC, "GrandTec Multi Capture" }, { 0x01020304, BTTV_BOARD_XGUARD, "Grandtec Grand X-Guard" }, @@ -2162,6 +2166,19 @@ struct tvcard bttv_tvcards[] = { .muxsel = { 2 }, .pll = PLL_28, }, + [BTTV_BOARD_IVCE8784] = { + .name = "IVCE-8784", + .video_inputs = 1, + .audio_inputs = 0, + .tuner = UNSET, + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .svhs = UNSET, + .gpiomask = 0xdf, + .muxsel = { 2 }, + .pll = PLL_28, + }, [BTTV_BOARD_XGUARD] = { .name = "Grand X-Guard / Trust 814PCI", .video_inputs = 16, diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 529bf6cf634d..a7bcad171823 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -180,6 +180,7 @@ #define BTTV_BOARD_VD012 0x99 #define BTTV_BOARD_VD012_X1 0x9a #define BTTV_BOARD_VD012_X2 0x9b +#define BTTV_BOARD_IVCE8784 0x9c /* more card-specific defines */ -- cgit v1.2.3-59-g8ed1b From f7fe3e6f3c3e9ef6ba5ca187b514d225296d18dd Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Mon, 19 Jan 2009 09:31:55 -0300 Subject: V4L/DVB (10323): em28xx: Add entry for GADMEI TVR200 Added entry for GADMEI TVR200. Thanks to Yohanes Nugroho for testing and data collection. Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 1 + drivers/media/video/em28xx/em28xx-cards.c | 20 ++++++++++++++++++++ drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 22 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 75bded8a4aa2..f7a7f48f4d74 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -58,3 +58,4 @@ 58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041] 60 -> Hauppauge WinTV HVR 850 (em2883) [2040:651f] 61 -> Pixelview PlayTV Box 4 USB 2.0 (em2820/em2840) + 62 -> Gadmei TVR200 (em2820/em2840) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 3b3ca3f46d52..3beaeeda2eee 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -183,6 +183,25 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2820_BOARD_GADMEI_TVR200] = { + .name = "Gadmei TVR200", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2820_BOARD_TERRATEC_CINERGY_250] = { .name = "Terratec Cinergy 250 USB", .tuner_type = TUNER_LG_PAL_NEW_TAPC, @@ -1349,6 +1368,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, {0x1ba50080, EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT}, + {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, }; int em28xx_tuner_callback(void *ptr, int component, int command, int arg) diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index dd2cd36fb1bb..989e67fdff3e 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -98,6 +98,7 @@ #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 #define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 #define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61 +#define EM2820_BOARD_GADMEI_TVR200 62 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3-59-g8ed1b From d661e62205498ce6518b9859bc30444e59737d8b Mon Sep 17 00:00:00 2001 From: Kyle Guinn Date: Fri, 16 Jan 2009 05:36:14 -0300 Subject: V4L/DVB (10366): gspca - mr97310a: New subdriver. This patch adds support for USB webcams based on the MR97310A chip. It was tested with an Aiptek PenCam VGA+ webcam. Signed-off-by: Kyle Guinn Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/Kconfig | 9 + drivers/media/video/gspca/Makefile | 98 ++++----- drivers/media/video/gspca/gspca.c | 1 + drivers/media/video/gspca/mr97310a.c | 378 +++++++++++++++++++++++++++++++++++ 5 files changed, 439 insertions(+), 48 deletions(-) create mode 100644 drivers/media/video/gspca/mr97310a.c (limited to 'Documentation') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 1c58a7630146..af80c3344567 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -193,6 +193,7 @@ spca500 084d:0003 D-Link DSC-350 spca500 08ca:0103 Aiptek PocketDV sunplus 08ca:0104 Aiptek PocketDVII 1.3 sunplus 08ca:0106 Aiptek Pocket DV3100+ +mr97310a 08ca:0111 Aiptek PenCam VGA+ sunplus 08ca:2008 Aiptek Mini PenCam 2 M sunplus 08ca:2010 Aiptek PocketCam 3M sunplus 08ca:2016 Aiptek PocketCam 2 Mega diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index ee6a691dff22..11c5d2fc20de 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -56,6 +56,15 @@ config USB_GSPCA_MARS To compile this driver as a module, choose M here: the module will be called gspca_mars. +config USB_GSPCA_MR97310A + tristate "Mars-Semi MR97310A USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the MR97310A chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_mr97310a. + config USB_GSPCA_OV519 tristate "OV519 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index bd8d9ee40504..b3cbcc1764f4 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -1,50 +1,52 @@ -obj-$(CONFIG_USB_GSPCA) += gspca_main.o -obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o -obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o -obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o -obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o -obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o -obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o -obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o -obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o -obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o -obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o -obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o -obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o -obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o -obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o -obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o -obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o -obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o -obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o -obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o -obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o -obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o -obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o +obj-$(CONFIG_USB_GSPCA) += gspca_main.o +obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o +obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o +obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o +obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o +obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o +obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o +obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o +obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o +obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o +obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o +obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o +obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o +obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o +obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o +obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o +obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o +obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o +obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o +obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o +obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o +obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o +obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o -gspca_main-objs := gspca.o -gspca_conex-objs := conex.o -gspca_etoms-objs := etoms.o -gspca_finepix-objs := finepix.o -gspca_mars-objs := mars.o -gspca_ov519-objs := ov519.o -gspca_ov534-objs := ov534.o -gspca_pac207-objs := pac207.o -gspca_pac7311-objs := pac7311.o -gspca_sonixb-objs := sonixb.o -gspca_sonixj-objs := sonixj.o -gspca_spca500-objs := spca500.o -gspca_spca501-objs := spca501.o -gspca_spca505-objs := spca505.o -gspca_spca506-objs := spca506.o -gspca_spca508-objs := spca508.o -gspca_spca561-objs := spca561.o -gspca_stk014-objs := stk014.o -gspca_sunplus-objs := sunplus.o -gspca_t613-objs := t613.o -gspca_tv8532-objs := tv8532.o -gspca_vc032x-objs := vc032x.o -gspca_zc3xx-objs := zc3xx.o +gspca_main-objs := gspca.o +gspca_conex-objs := conex.o +gspca_etoms-objs := etoms.o +gspca_finepix-objs := finepix.o +gspca_mars-objs := mars.o +gspca_mr97310a-objs := mr97310a.o +gspca_ov519-objs := ov519.o +gspca_ov534-objs := ov534.o +gspca_pac207-objs := pac207.o +gspca_pac7311-objs := pac7311.o +gspca_sonixb-objs := sonixb.o +gspca_sonixj-objs := sonixj.o +gspca_spca500-objs := spca500.o +gspca_spca501-objs := spca501.o +gspca_spca505-objs := spca505.o +gspca_spca506-objs := spca506.o +gspca_spca508-objs := spca508.o +gspca_spca561-objs := spca561.o +gspca_stk014-objs := stk014.o +gspca_sunplus-objs := sunplus.o +gspca_t613-objs := t613.o +gspca_tv8532-objs := tv8532.o +gspca_vc032x-objs := vc032x.o +gspca_zc3xx-objs := zc3xx.o -obj-$(CONFIG_USB_M5602) += m5602/ -obj-$(CONFIG_USB_STV06XX) += stv06xx/ +obj-$(CONFIG_USB_M5602) += m5602/ +obj-$(CONFIG_USB_STV06XX) += stv06xx/ diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 5dd2abc17ae6..6c03d57ae506 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -318,6 +318,7 @@ static int gspca_is_compressed(__u32 format) case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_SPCA561: case V4L2_PIX_FMT_PAC207: + case V4L2_PIX_FMT_MR97310A: return 1; } return 0; diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c new file mode 100644 index 000000000000..24180bf0cdab --- /dev/null +++ b/drivers/media/video/gspca/mr97310a.c @@ -0,0 +1,378 @@ +/* + * Mars MR97310A library + * + * Copyright (C) 2009 Kyle Guinn + * + * 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 + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "mr97310a" + +#include "gspca.h" + +MODULE_AUTHOR("Kyle Guinn "); +MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u8 sof_read; + u8 header_read; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 4}, + {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* the bytes to write are in gspca_dev->usb_buf */ +static int reg_w(struct gspca_dev *gspca_dev, int len) +{ + int rc; + + rc = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 4), + gspca_dev->usb_buf, len, 0, 500); + if (rc < 0) + PDEBUG(D_ERR, "reg write [%02x] error %d", + gspca_dev->usb_buf[0], rc); + return rc; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + cam = &gspca_dev->cam; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 *data = gspca_dev->usb_buf; + int err_code; + + sd->sof_read = 0; + + /* Note: register descriptions guessed from MR97113A driver */ + + data[0] = 0x01; + data[1] = 0x01; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x00; + data[1] = 0x0d; + data[2] = 0x01; + data[5] = 0x2b; + data[7] = 0x00; + data[9] = 0x50; /* reg 8, no scale down */ + data[10] = 0xc0; + + switch (gspca_dev->width) { + case 160: + data[9] |= 0x0c; /* reg 8, 4:1 scale down */ + /* fall thru */ + case 320: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 640: + default: + data[3] = 0x50; /* reg 2, H size */ + data[4] = 0x78; /* reg 3, V size */ + data[6] = 0x04; /* reg 5, H start */ + data[8] = 0x03; /* reg 7, V start */ + break; + + case 176: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 352: + data[3] = 0x2c; /* reg 2, H size */ + data[4] = 0x48; /* reg 3, V size */ + data[6] = 0x94; /* reg 5, H start */ + data[8] = 0x63; /* reg 7, V start */ + break; + } + + err_code = reg_w(gspca_dev, 11); + if (err_code < 0) + return err_code; + + data[0] = 0x0a; + data[1] = 0x80; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x14; + data[1] = 0x0a; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x1b; + data[1] = 0x00; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x15; + data[1] = 0x16; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x16; + data[1] = 0x10; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x17; + data[1] = 0x3a; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x18; + data[1] = 0x68; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x00; + data[2] = 0x02; + data[3] = 0x06; + data[4] = 0x59; + data[5] = 0x0c; + data[6] = 0x16; + data[7] = 0x00; + data[8] = 0x07; + data[9] = 0x00; + data[10] = 0x01; + err_code = reg_w(gspca_dev, 11); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x04; + data[2] = 0x11; + data[3] = 0x01; + err_code = reg_w(gspca_dev, 4); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x00; + data[2] = 0x0a; + data[3] = 0x00; + data[4] = 0x01; + data[5] = 0x00; + data[6] = 0x00; + data[7] = 0x01; + data[8] = 0x00; + data[9] = 0x0a; + err_code = reg_w(gspca_dev, 10); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x04; + data[2] = 0x11; + data[3] = 0x01; + err_code = reg_w(gspca_dev, 4); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x00; + data[2] = 0x12; + data[3] = 0x00; + data[4] = 0x63; + data[5] = 0x00; + data[6] = 0x70; + data[7] = 0x00; + data[8] = 0x01; + err_code = reg_w(gspca_dev, 10); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x04; + data[2] = 0x11; + data[3] = 0x01; + err_code = reg_w(gspca_dev, 4); + if (err_code < 0) + return err_code; + + data[0] = 0x00; + data[1] = 0x4d; /* ISOC transfering enable... */ + err_code = reg_w(gspca_dev, 2); + return err_code; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int result; + + gspca_dev->usb_buf[0] = 1; + gspca_dev->usb_buf[1] = 0; + result = reg_w(gspca_dev, 2); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop failed"); +} + +/* Include pac common sof detection functions */ +#include "pac_common.h" + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char *sof; + + sof = pac_find_sof(gspca_dev, data, len); + if (sof) { + int n; + + /* finish decoding current frame */ + n = sof - data; + if (n > sizeof pac_sof_marker) + n -= sizeof pac_sof_marker; + else + n = 0; + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, n); + sd->header_read = 0; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + len -= sof - data; + data = sof; + } + if (sd->header_read < 7) { + int needed; + + /* skip the rest of the header */ + needed = 7 - sd->header_read; + if (len <= needed) { + sd->header_read += len; + return; + } + data += needed; + len -= needed; + sd->header_read = 7; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0111)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); -- cgit v1.2.3-59-g8ed1b From db91235ee8350149213435c4cf178a7627c968b9 Mon Sep 17 00:00:00 2001 From: Lierdakil Date: Sun, 25 Jan 2009 14:37:26 -0300 Subject: V4L/DVB (10388): gspca - pac207: Webcam 093a:2474 added. Signed-off-by: Lierdakil Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/pac207.c | 1 + 2 files changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index af80c3344567..3136c80280b1 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -216,6 +216,7 @@ pac207 093a:2468 PAC207 pac207 093a:2470 Genius GF112 pac207 093a:2471 Genius VideoCam ge111 pac207 093a:2472 Genius VideoCam ge110 +pac207 093a:2474 Genius iLook 111 pac207 093a:2476 Genius e-Messenger 112 pac7311 093a:2600 PAC7311 Typhoon pac7311 093a:2601 Philips SPC 610 NC diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 93616cebf360..95a97ab684cd 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -535,6 +535,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2470)}, {USB_DEVICE(0x093a, 0x2471)}, {USB_DEVICE(0x093a, 0x2472)}, + {USB_DEVICE(0x093a, 0x2474)}, {USB_DEVICE(0x093a, 0x2476)}, {USB_DEVICE(0x145f, 0x013a)}, {USB_DEVICE(0x2001, 0xf115)}, -- cgit v1.2.3-59-g8ed1b From de43cd37b88408280cb4e533c9fee820fb8d5c73 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 29 Jan 2009 06:02:37 -0300 Subject: V4L/DVB (10421): gspca - documentation: Add the webcam 041e:405b. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 3136c80280b1..12c8ff7058b5 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -32,6 +32,7 @@ spca561 041e:403b Creative Webcam Vista (VF0010) zc3xx 041e:4051 Creative Live!Cam Notebook Pro (VF0250) ov519 041e:4052 Creative Live! VISTA IM zc3xx 041e:4053 Creative Live!Cam Video IM +vc032x 041e:405b Creative Live! Cam Notebook Ultra (VC0130) ov519 041e:405f Creative Live! VISTA VF0330 ov519 041e:4060 Creative Live! VISTA VF0350 ov519 041e:4061 Creative Live! VISTA VF0400 -- cgit v1.2.3-59-g8ed1b From d7493e518fa98d2c30c545c518df075903bae513 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 6 Feb 2009 15:35:22 -0300 Subject: V4L/DVB (10487): doc: update hm12 documentation. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/README.hm12 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/cx2341x/README.hm12 b/Documentation/video4linux/cx2341x/README.hm12 index 0e213ed095e6..b36148ea0750 100644 --- a/Documentation/video4linux/cx2341x/README.hm12 +++ b/Documentation/video4linux/cx2341x/README.hm12 @@ -32,6 +32,10 @@ Y, U and V planes. This code assumes frames of 720x576 (PAL) pixels. The width of a frame is always 720 pixels, regardless of the actual specified width. +If the height is not a multiple of 32 lines, then the captured video is +missing macroblocks at the end and is unusable. So the height must be a +multiple of 32. + -------------------------------------------------------------------------- #include -- cgit v1.2.3-59-g8ed1b From 89aec3e1baaddeaa5636487f0e23f807eb758168 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 7 Feb 2009 07:07:04 -0300 Subject: V4L/DVB (10489): doc: use consistent naming conventions for vdev and v4l2_dev. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index cc350624237d..73f9b642392b 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -134,7 +134,7 @@ The recommended approach is as follows: static atomic_t drv_instance = ATOMIC_INIT(0); -static int __devinit drv_probe(struct pci_dev *dev, +static int __devinit drv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { ... @@ -218,7 +218,7 @@ to add new ops and categories. A sub-device driver initializes the v4l2_subdev struct using: - v4l2_subdev_init(subdev, &ops); + v4l2_subdev_init(sd, &ops); Afterwards you need to initialize subdev->name with a unique name and set the module owner. This is done for you if you use the i2c helper functions. @@ -226,7 +226,7 @@ module owner. This is done for you if you use the i2c helper functions. A device (bridge) driver needs to register the v4l2_subdev with the v4l2_device: - int err = v4l2_device_register_subdev(device, subdev); + int err = v4l2_device_register_subdev(v4l2_dev, sd); This can fail if the subdev module disappeared before it could be registered. After this function was called successfully the subdev->dev field points to @@ -234,17 +234,17 @@ the v4l2_device. You can unregister a sub-device using: - v4l2_device_unregister_subdev(subdev); + v4l2_device_unregister_subdev(sd); -Afterwards the subdev module can be unloaded and subdev->dev == NULL. +Afterwards the subdev module can be unloaded and sd->dev == NULL. You can call an ops function either directly: - err = subdev->ops->core->g_chip_ident(subdev, &chip); + err = sd->ops->core->g_chip_ident(sd, &chip); but it is better and easier to use this macro: - err = v4l2_subdev_call(subdev, core, g_chip_ident, &chip); + err = v4l2_subdev_call(sd, core, g_chip_ident, &chip); The macro will to the right NULL pointer checks and returns -ENODEV if subdev is NULL, -ENOIOCTLCMD if either subdev->core or subdev->core->g_chip_ident is @@ -252,12 +252,12 @@ NULL, or the actual result of the subdev->ops->core->g_chip_ident ops. It is also possible to call all or a subset of the sub-devices: - v4l2_device_call_all(dev, 0, core, g_chip_ident, &chip); + v4l2_device_call_all(v4l2_dev, 0, core, g_chip_ident, &chip); Any subdev that does not support this ops is skipped and error results are ignored. If you want to check for errors use this: - err = v4l2_device_call_until_err(dev, 0, core, g_chip_ident, &chip); + err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_chip_ident, &chip); Any error except -ENOIOCTLCMD will exit the loop with that error. If no errors (except -ENOIOCTLCMD) occured, then 0 is returned. @@ -505,8 +505,8 @@ There are a few useful helper functions: You can set/get driver private data in the video_device struct using: -void *video_get_drvdata(struct video_device *dev); -void video_set_drvdata(struct video_device *dev, void *data); +void *video_get_drvdata(struct video_device *vdev); +void video_set_drvdata(struct video_device *vdev, void *data); Note that you can safely call video_set_drvdata() before calling video_register_device(). -- cgit v1.2.3-59-g8ed1b From 0bf4f6ce6d43b135867a78fa1b4ac58e22d2e329 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 11 Feb 2009 14:13:20 -0300 Subject: V4L/DVB (10514): em28xx: Add support for Kaiomy TVnPC U2 stick Thanks to Peter Senna Tschudin for borrow me one of those devices. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 1 + drivers/media/video/em28xx/em28xx-cards.c | 43 +++++++++++++++++++++++++++++++ drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 45 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index f7a7f48f4d74..e914b1374347 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -57,5 +57,6 @@ 57 -> Kworld PlusTV HD Hybrid 330 (em2883) [eb1a:a316] 58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041] 60 -> Hauppauge WinTV HVR 850 (em2883) [2040:651f] + 61 -> Kaiomy TVnPC U2 (em2860) [eb1a:e303] 61 -> Pixelview PlayTV Box 4 USB 2.0 (em2820/em2840) 62 -> Gadmei TVR200 (em2820/em2840) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 369db26cd721..c62243ef73b4 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -1252,6 +1252,33 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2860_BOARD_KAIOMY_TVNPC_U2] = { + .name = "Kaiomy TVnPC U2", + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .mts_firmware = 1, + .decoder = EM28XX_TVP5150, + .tuner_gpio = default_tuner_gpio, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + .radio = { + .type = EM28XX_RADIO, + .amux = EM28XX_AMUX_LINE_IN, + } + } }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1279,6 +1306,8 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0xe300), .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, + { USB_DEVICE(0xeb1a, 0xe303), + .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 }, { USB_DEVICE(0xeb1a, 0xe305), .driver_info = EM2880_BOARD_KWORLD_DVB_305U }, { USB_DEVICE(0xeb1a, 0xe310), @@ -1524,6 +1553,20 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* enables audio for that devices */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; + + case EM2860_BOARD_KAIOMY_TVNPC_U2: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_regs(dev, 0x0d, "\x42", 1); + em28xx_write_regs(dev, 0x08, "\xfd", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\xff", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\x7f", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\x6b", 1); + + break; } em28xx_gpio_set(dev, dev->board.tuner_gpio); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 989e67fdff3e..24fc8b429466 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -99,6 +99,7 @@ #define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 #define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61 #define EM2820_BOARD_GADMEI_TVR200 62 +#define EM2860_BOARD_KAIOMY_TVNPC_U2 61 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3-59-g8ed1b From 56ee38071fe0cf1746d53c5b40a46a835b24fbe4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 11 Feb 2009 14:18:36 -0300 Subject: V4L/DVB (10516): em28xx: Add support for Easy Cap Capture DC-60 Thanks to Peter Senna Tschudin for borrow me one of those devices. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 3 ++- drivers/media/video/em28xx/em28xx-cards.c | 21 ++++++++++++++++++++- drivers/media/video/em28xx/em28xx.h | 3 ++- 3 files changed, 24 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index e914b1374347..b336dcab139c 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -57,6 +57,7 @@ 57 -> Kworld PlusTV HD Hybrid 330 (em2883) [eb1a:a316] 58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041] 60 -> Hauppauge WinTV HVR 850 (em2883) [2040:651f] - 61 -> Kaiomy TVnPC U2 (em2860) [eb1a:e303] 61 -> Pixelview PlayTV Box 4 USB 2.0 (em2820/em2840) 62 -> Gadmei TVR200 (em2820/em2840) + 63 -> Kaiomy TVnPC U2 (em2860) [eb1a:e303] + 64 -> Easy Cap Capture DC-60 (em2860) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 2048a8761099..1f38e35355ce 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -1279,7 +1279,22 @@ struct em28xx_board em28xx_boards[] = { .type = EM28XX_RADIO, .amux = EM28XX_AMUX_LINE_IN, } - } + }, + [EM2860_BOARD_EASYCAP] = { + .name = "Easy Cap Capture DC-60", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1568,6 +1583,10 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_regs(dev, 0x08, "\x6b", 1); break; + case EM2860_BOARD_EASYCAP: + em28xx_write_regs(dev, 0x08, "\xf8", 1); + break; + } em28xx_gpio_set(dev, dev->board.tuner_gpio); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 24fc8b429466..3d94afb55b56 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -99,7 +99,8 @@ #define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 #define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61 #define EM2820_BOARD_GADMEI_TVR200 62 -#define EM2860_BOARD_KAIOMY_TVNPC_U2 61 +#define EM2860_BOARD_KAIOMY_TVNPC_U2 63 +#define EM2860_BOARD_EASYCAP 64 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3-59-g8ed1b From 7aa0eabde08259c47586df934921c67cff36e7dc Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Tue, 10 Feb 2009 22:00:06 -0300 Subject: V4L/DVB (10524): em28xx: Add DVC 101 model to Pinnacle Dazzle description Added DVC 101 model to Pinnacle Dazzle description Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 2 +- drivers/media/video/em28xx/em28xx-cards.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index b336dcab139c..69601089f0a5 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -7,7 +7,7 @@ 6 -> Terratec Cinergy 200 USB (em2800) 7 -> Leadtek Winfast USB II (em2800) [0413:6023] 8 -> Kworld USB2800 (em2800) - 9 -> Pinnacle Dazzle DVC 90/DVC 100 (em2820/em2840) [2304:0207,2304:021a] + 9 -> Pinnacle Dazzle DVC 90/DVC 100/DVC 101 (em2820/em2840) [2304:0207,2304:021a] 10 -> Hauppauge WinTV HVR 900 (em2880) [2040:6500] 11 -> Terratec Hybrid XS (em2880) [0ccd:0042] 12 -> Kworld PVR TV 2800 RF (em2820/em2840) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 1f38e35355ce..89a8f2657b13 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -916,7 +916,7 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/DVC 100", + .name = "Pinnacle Dazzle DVC 90/DVC 100/DVC 101", .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { -- cgit v1.2.3-59-g8ed1b From f74a61e3c6f218053742c2caf3e247fb41bf395e Mon Sep 17 00:00:00 2001 From: Indika Katugampala Date: Wed, 11 Feb 2009 11:13:05 -0300 Subject: V4L/DVB (10528): em28xx: support added for IO-DATA GV/MVP SZ - EMPIA-2820 chipset [dougsland@redhat.com: Fixed CodingStyle] Signed-off-by: Indika Katugampala Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 1 + drivers/media/video/em28xx/em28xx-cards.c | 32 +++++++++++++++++++++++++++++++ drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 34 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 69601089f0a5..f97c8533d01a 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -61,3 +61,4 @@ 62 -> Gadmei TVR200 (em2820/em2840) 63 -> Kaiomy TVnPC U2 (em2860) [eb1a:e303] 64 -> Easy Cap Capture DC-60 (em2860) + 65 -> IO-DATA GV-MVP/SZ (em2820/em2840) [04bb:0515] diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 419304d31256..c16c28e1ec48 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -1297,6 +1297,26 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2820_BOARD_IODATA_GVMVP_SZ] = { + .name = "IO-DATA GV-MVP/SZ", + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .tuner_gpio = default_tuner_gpio, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + }, { /* Composite has not been tested yet */ + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_VIDEO, + }, { /* S-video has not been tested yet */ + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_VIDEO, + } }, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1396,6 +1416,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII }, { USB_DEVICE(0x093b, 0xa005), .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, + { USB_DEVICE(0x04bb, 0x0515), + .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -1589,6 +1611,16 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_regs(dev, 0x08, "\xf8", 1); break; + case EM2820_BOARD_IODATA_GVMVP_SZ: + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + msleep(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + msleep(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + msleep(70); + break; } em28xx_gpio_set(dev, dev->board.tuner_gpio); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 5115db3a76a6..3e82d818c0cd 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -101,6 +101,7 @@ #define EM2820_BOARD_GADMEI_TVR200 62 #define EM2860_BOARD_KAIOMY_TVNPC_U2 63 #define EM2860_BOARD_EASYCAP 64 +#define EM2820_BOARD_IODATA_GVMVP_SZ 65 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3-59-g8ed1b From 00ec8d0799d56448525b51abbf2075951f637b37 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Thu, 12 Feb 2009 14:55:45 -0300 Subject: V4L/DVB (10530): Documentation and code cleanups - "DealExtreme" sells the "PCear" radio and that comes from "Sanei Electric". - MPlayer is also usable as radio application. - Consistent usage of tabulators and blanks in the code. Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/si470x.txt | 11 ++++++++--- drivers/media/radio/radio-si470x.c | 11 ++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/si470x.txt b/Documentation/video4linux/si470x.txt index 49679e6aaa76..3a7823e01b4d 100644 --- a/Documentation/video4linux/si470x.txt +++ b/Documentation/video4linux/si470x.txt @@ -1,6 +1,6 @@ Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers -Copyright (c) 2008 Tobias Lorenz +Copyright (c) 2009 Tobias Lorenz Information from Silicon Labs @@ -41,7 +41,7 @@ chips are known to work: - 10c4:818a: Silicon Labs USB FM Radio Reference Design - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF) - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700) -- 10c5:819a: DealExtreme USB Radio +- 10c5:819a: Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) Software @@ -52,6 +52,7 @@ Testing is usually done with most application under Debian/testing: - gradio - GTK FM radio tuner - kradio - Comfortable Radio Application for KDE - radio - ncurses-based radio application +- mplayer - The Ultimate Movie Player For Linux There is also a library libv4l, which can be used. It's going to have a function for frequency seeking, either by using hardware functionality as in radio-si470x @@ -69,7 +70,7 @@ Audio Listing USB Audio is provided by the ALSA snd_usb_audio module. It is recommended to also select SND_USB_AUDIO, as this is required to get sound from the radio. For listing you have to redirect the sound, for example using one of the following -commands. +commands. Please adjust the audio devices to your needs (/dev/dsp* and hw:x,x). If you just want to test audio (very poor quality): cat /dev/dsp1 > /dev/dsp @@ -80,6 +81,10 @@ sox -2 --endian little -r 96000 -t oss /dev/dsp1 -t oss /dev/dsp If you use arts try: arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B - +If you use mplayer try: +mplayer -radio adevice=hw=1.0:arate=96000 \ + -rawaudio rate=96000 \ + radio:///capture Module Parameters ================= diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index dc72803ad209..8743a521e4af 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -5,8 +5,9 @@ * - Silicon Labs USB FM Radio Reference Design * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF) * - KWorld USB FM Radio SnapMusic Mobile 700 (FM700) + * - Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) * - * Copyright (c) 2008 Tobias Lorenz + * Copyright (c) 2009 Tobias Lorenz * * 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 @@ -29,7 +30,7 @@ * 2008-01-12 Tobias Lorenz * Version 1.0.0 * - First working version - * 2008-01-13 Tobias Lorenz + * 2008-01-13 Tobias Lorenz * Version 1.0.1 * - Improved error handling, every function now returns errno * - Improved multi user access (start/mute/stop) @@ -145,7 +146,7 @@ static struct usb_device_id si470x_usb_driver_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, - /* DealExtreme USB Radio */ + /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, /* Terminating entry */ { } @@ -345,7 +346,7 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); /* Report 19: stream */ #define STREAM_REPORT_SIZE 3 -#define STREAM_REPORT 19 +#define STREAM_REPORT 19 /* Report 20: scratch */ #define SCRATCH_PAGE_SIZE 63 @@ -414,7 +415,7 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); /* bootloader commands */ #define GET_SW_VERSION_COMMAND 0x00 -#define SET_PAGE_COMMAND 0x01 +#define SET_PAGE_COMMAND 0x01 #define ERASE_PAGE_COMMAND 0x02 #define WRITE_PAGE_COMMAND 0x03 #define CRC_ON_PAGE_COMMAND 0x04 -- cgit v1.2.3-59-g8ed1b From 1d6af821a91df15e3fc2720c223ec514ae83dc86 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Thu, 12 Feb 2009 14:22:42 -0300 Subject: V4L/DVB (10556): em28xx-cards: Add Pinnacle Dazzle Video Creator Plus DVC107 description Added board Pinnacle Dazzle Video Creator Plus DVC107 to name description field. Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 2 +- drivers/media/video/em28xx/em28xx-cards.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index f97c8533d01a..77874bd20550 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -7,7 +7,7 @@ 6 -> Terratec Cinergy 200 USB (em2800) 7 -> Leadtek Winfast USB II (em2800) [0413:6023] 8 -> Kworld USB2800 (em2800) - 9 -> Pinnacle Dazzle DVC 90/DVC 100/DVC 101 (em2820/em2840) [2304:0207,2304:021a] + 9 -> Pinnacle Dazzle DVC 90/DVC 100/DVC 101/DVC 107 (em2820/em2840) [2304:0207,2304:021a] 10 -> Hauppauge WinTV HVR 900 (em2880) [2040:6500] 11 -> Terratec Hybrid XS (em2880) [0ccd:0042] 12 -> Kworld PVR TV 2800 RF (em2820/em2840) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index c16c28e1ec48..615cf362e79c 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -916,7 +916,7 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/DVC 100/DVC 101", + .name = "Pinnacle Dazzle DVC 90/DVC 100/DVC 101/DVC 107", .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { -- cgit v1.2.3-59-g8ed1b From 44061c05ac8dedcc45c439e871f654c9521cc726 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 14 Feb 2009 07:29:07 -0300 Subject: V4L/DVB (10570): v4l2-framework: documments videobuf usage on drivers Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 92 +++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 73f9b642392b..8d2db7e09e89 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -47,7 +47,9 @@ All drivers have the following structure: 3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and /dev/vtxX) and keeping track of device-node specific data. -4) Filehandle-specific structs containing per-filehandle data. +4) Filehandle-specific structs containing per-filehandle data; + +5) video buffer handling. This is a rough schematic of how it all relates: @@ -525,3 +527,91 @@ void *video_drvdata(struct file *file); You can go from a video_device struct to the v4l2_device struct using: struct v4l2_device *v4l2_dev = vdev->v4l2_dev; + +video buffer helper functions +----------------------------- + +The v4l2 core API provides a standard method for dealing with video +buffers. Those methods allow a driver to implement read(), mmap() and +overlay() on a consistent way. + +There are currently methods for using video buffers on devices that +supports DMA with scatter/gather method (videobuf-dma-sg), DMA with +linear access (videobuf-dma-contig), and vmalloced buffers, mostly +used on USB drivers (videobuf-vmalloc). + +Any driver using videobuf should provide operations (callbacks) for +four handlers: + +ops->buf_setup - calculates the size of the video buffers and avoid they + to waste more than some maximum limit of RAM; +ops->buf_prepare - fills the video buffer structs and calls + videobuf_iolock() to alloc and prepare mmaped memory; +ops->buf_queue - advices the driver that another buffer were + requested (by read() or by QBUF); +ops->buf_release - frees any buffer that were allocated. + +In order to use it, the driver need to have a code (generally called at +interrupt context) that will properly handle the buffer request lists, +announcing that a new buffer were filled. + +The irq handling code should handle the videobuf task lists, in order +to advice videobuf that a new frame were filled, in order to honor to a +request. The code is generally like this one: + if (list_empty(&dma_q->active)) + return; + + buf = list_entry(dma_q->active.next, struct vbuffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) + return; + + /* Some logic to handle the buf may be needed here */ + + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + wake_up(&buf->vb.done); + +Those are the videobuffer functions used on drivers, implemented on +videobuf-core: + +- videobuf_queue_core_init() + Initializes the videobuf infrastructure. This function should be + called before any other videobuf function. + +- videobuf_iolock() + Prepares the videobuf memory for the proper method (read, mmap, overlay). + +- videobuf_queue_is_busy() + Checks if a videobuf is streaming. + +- videobuf_queue_cancel() + Stops video handling. + +- videobuf_mmap_free() + frees mmap buffers. + +- videobuf_stop() + Stops video handling, ends mmap and frees mmap and other buffers. + +- V4L2 api functions. Those functions correspond to VIDIOC_foo ioctls: + videobuf_reqbufs(), videobuf_querybuf(), videobuf_qbuf(), + videobuf_dqbuf(), videobuf_streamon(), videobuf_streamoff(). + +- V4L1 api function (corresponds to VIDIOCMBUF ioctl): + videobuf_cgmbuf() + This function is used to provide backward compatibility with V4L1 + API. + +- Some help functions for read()/poll() operations: + videobuf_read_stream() + For continuous stream read() + videobuf_read_one() + For snapshot read() + videobuf_poll_stream() + polling help function + +The better way to understand it is to take a look at vivi driver. One +of the main reasons for vivi is to be a videobuf usage example. the +vivi_thread_tick() does the task that the IRQ callback would do on PCI +drivers (or the irq callback on USB). -- cgit v1.2.3-59-g8ed1b From a7a1c0e60c706fc04c42c020cd9c45b8bfe14e8b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 14 Feb 2009 07:51:28 -0300 Subject: V4L/DVB (10571): v4l2-framework.txt: Fixes the videobuf init functions Documents the driver usage functions, instead of the generic one used by the videobuf specific handlers. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 30 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 8d2db7e09e89..48cdf86248cb 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -558,26 +558,38 @@ announcing that a new buffer were filled. The irq handling code should handle the videobuf task lists, in order to advice videobuf that a new frame were filled, in order to honor to a request. The code is generally like this one: - if (list_empty(&dma_q->active)) + if (list_empty(&dma_q->active)) return; - buf = list_entry(dma_q->active.next, struct vbuffer, vb.queue); + buf = list_entry(dma_q->active.next, struct vbuffer, vb.queue); - if (!waitqueue_active(&buf->vb.done)) + if (!waitqueue_active(&buf->vb.done)) return; /* Some logic to handle the buf may be needed here */ - list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); - wake_up(&buf->vb.done); + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + wake_up(&buf->vb.done); Those are the videobuffer functions used on drivers, implemented on videobuf-core: -- videobuf_queue_core_init() - Initializes the videobuf infrastructure. This function should be - called before any other videobuf function. +- Videobuf init functions + videobuf_queue_sg_init() + Initializes the videobuf infrastructure. This function should be + called before any other videobuf function on drivers that uses DMA + Scatter/Gather buffers. + + videobuf_queue_dma_contig_init + Initializes the videobuf infrastructure. This function should be + called before any other videobuf function on drivers that need DMA + contiguous buffers. + + videobuf_queue_vmalloc_init() + Initializes the videobuf infrastructure. This function should be + called before any other videobuf function on USB (and other drivers) + that need a vmalloced type of videobuf. - videobuf_iolock() Prepares the videobuf memory for the proper method (read, mmap, overlay). -- cgit v1.2.3-59-g8ed1b From 3a63e4492fbc7aa7f99d4368822da1382ec6fe03 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Feb 2009 11:54:23 -0300 Subject: V4L/DVB (10643): v4l2-device: allow a NULL parent device when registering. Some drivers (e.g. for ISA devices) have no parent device because there is no associated bus driver. Allow the parent device to be NULL in those cases when registering v4l2_device. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 10 +++++--- drivers/media/video/v4l2-device.c | 37 ++++++++++++++++++---------- include/media/v4l2-device.h | 31 +++++++++++++---------- 3 files changed, 48 insertions(+), 30 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 48cdf86248cb..e1620e2a38ff 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -84,12 +84,14 @@ You must register the device instance: v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); Registration will initialize the v4l2_device struct and link dev->driver_data -to v4l2_dev. Registration will also set v4l2_dev->name to a value derived from -dev (driver name followed by the bus_id, to be precise). You may change the -name after registration if you want. +to v4l2_dev. If v4l2_dev->name is empty then it will be set to a value derived +from dev (driver name followed by the bus_id, to be precise). If you set it +up before calling v4l2_device_register then it will be untouched. If dev is +NULL, then you *must* setup v4l2_dev->name before calling v4l2_device_register. The first 'dev' argument is normally the struct device pointer of a pci_dev, -usb_device or platform_device. +usb_device or platform_device. It is rare for dev to be NULL, but it happens +with ISA devices, for example. You unregister with: diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 8a4b74f3129f..3330ffb7d010 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -26,15 +26,24 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) { - if (dev == NULL || v4l2_dev == NULL) + if (v4l2_dev == NULL) return -EINVAL; - /* Warn if we apparently re-register a device */ - WARN_ON(dev_get_drvdata(dev) != NULL); + INIT_LIST_HEAD(&v4l2_dev->subdevs); spin_lock_init(&v4l2_dev->lock); v4l2_dev->dev = dev; - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", + if (dev == NULL) { + /* If dev == NULL, then name must be filled in by the caller */ + WARN_ON(!v4l2_dev->name[0]); + return 0; + } + + /* Set name to driver name + device name if it is empty. */ + if (!v4l2_dev->name[0]) + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", dev->driver->name, dev_name(dev)); + if (dev_get_drvdata(dev)) + v4l2_warn(v4l2_dev, "Non-NULL drvdata on register\n"); dev_set_drvdata(dev, v4l2_dev); return 0; } @@ -44,10 +53,11 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) { struct v4l2_subdev *sd, *next; - if (v4l2_dev == NULL || v4l2_dev->dev == NULL) + if (v4l2_dev == NULL) return; - dev_set_drvdata(v4l2_dev->dev, NULL); - /* unregister subdevs */ + if (v4l2_dev->dev) + dev_set_drvdata(v4l2_dev->dev, NULL); + /* Unregister subdevs */ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) v4l2_device_unregister_subdev(sd); @@ -55,19 +65,20 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) } EXPORT_SYMBOL_GPL(v4l2_device_unregister); -int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd) +int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, + struct v4l2_subdev *sd) { /* Check for valid input */ - if (dev == NULL || sd == NULL || !sd->name[0]) + if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) return -EINVAL; /* Warn if we apparently re-register a subdev */ WARN_ON(sd->dev != NULL); if (!try_module_get(sd->owner)) return -ENODEV; - sd->dev = dev; - spin_lock(&dev->lock); - list_add_tail(&sd->list, &dev->subdevs); - spin_unlock(&dev->lock); + sd->dev = v4l2_dev; + spin_lock(&v4l2_dev->lock); + list_add_tail(&sd->list, &v4l2_dev->subdevs); + spin_unlock(&v4l2_dev->lock); return 0; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 55e41afd95ef..5d7146dc2913 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -33,7 +33,9 @@ #define V4L2_DEVICE_NAME_SIZE (BUS_ID_SIZE + 16) struct v4l2_device { - /* dev->driver_data points to this struct */ + /* dev->driver_data points to this struct. + Note: dev might be NULL if there is no parent device + as is the case with e.g. ISA devices. */ struct device *dev; /* used to keep track of the registered subdevs */ struct list_head subdevs; @@ -44,7 +46,9 @@ struct v4l2_device { char name[V4L2_DEVICE_NAME_SIZE]; }; -/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev */ +/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev. + dev may be NULL in rare cases (ISA devices). In that case you + must fill in the v4l2_dev->name field before calling this function. */ int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); /* Set v4l2_dev->dev->driver_data to NULL and unregister all sub-devices */ void v4l2_device_unregister(struct v4l2_device *v4l2_dev); @@ -52,23 +56,24 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev); /* Register a subdev with a v4l2 device. While registered the subdev module is marked as in-use. An error is returned if the module is no longer loaded when you attempt to register it. */ -int __must_check v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd); +int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, + struct v4l2_subdev *sd); /* Unregister a subdev with a v4l2 device. Can also be called if the subdev wasn't registered. In that case it will do nothing. */ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); /* Iterate over all subdevs. */ -#define v4l2_device_for_each_subdev(sd, dev) \ - list_for_each_entry(sd, &(dev)->subdevs, list) +#define v4l2_device_for_each_subdev(sd, v4l2_dev) \ + list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) /* Call the specified callback for all subdevs matching the condition. Ignore any errors. Note that you cannot add or delete a subdev while walking the subdevs list. */ -#define __v4l2_device_call_subdevs(dev, cond, o, f, args...) \ +#define __v4l2_device_call_subdevs(v4l2_dev, cond, o, f, args...) \ do { \ struct v4l2_subdev *sd; \ \ - list_for_each_entry(sd, &(dev)->subdevs, list) \ + list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) \ if ((cond) && sd->ops->o && sd->ops->o->f) \ sd->ops->o->f(sd , ##args); \ } while (0) @@ -77,12 +82,12 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); If the callback returns an error other than 0 or -ENOIOCTLCMD, then return with that error code. Note that you cannot add or delete a subdev while walking the subdevs list. */ -#define __v4l2_device_call_subdevs_until_err(dev, cond, o, f, args...) \ +#define __v4l2_device_call_subdevs_until_err(v4l2_dev, cond, o, f, args...) \ ({ \ struct v4l2_subdev *sd; \ long err = 0; \ \ - list_for_each_entry(sd, &(dev)->subdevs, list) { \ + list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) { \ if ((cond) && sd->ops->o && sd->ops->o->f) \ err = sd->ops->o->f(sd , ##args); \ if (err && err != -ENOIOCTLCMD) \ @@ -94,16 +99,16 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); /* Call the specified callback for all subdevs matching grp_id (if 0, then match them all). Ignore any errors. Note that you cannot add or delete a subdev while walking the subdevs list. */ -#define v4l2_device_call_all(dev, grpid, o, f, args...) \ - __v4l2_device_call_subdevs(dev, \ +#define v4l2_device_call_all(v4l2_dev, grpid, o, f, args...) \ + __v4l2_device_call_subdevs(v4l2_dev, \ !(grpid) || sd->grp_id == (grpid), o, f , ##args) /* Call the specified callback for all subdevs matching grp_id (if 0, then match them all). If the callback returns an error other than 0 or -ENOIOCTLCMD, then return with that error code. Note that you cannot add or delete a subdev while walking the subdevs list. */ -#define v4l2_device_call_until_err(dev, grpid, o, f, args...) \ - __v4l2_device_call_subdevs_until_err(dev, \ +#define v4l2_device_call_until_err(v4l2_dev, grpid, o, f, args...) \ + __v4l2_device_call_subdevs_until_err(v4l2_dev, \ !(grpid) || sd->grp_id == (grpid), o, f , ##args) #endif -- cgit v1.2.3-59-g8ed1b From b01676005446ad51a32bb00577647c7aae7d2624 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Feb 2009 12:00:53 -0300 Subject: V4L/DVB (10644): v4l2-subdev: rename dev field to v4l2_dev Remain consistent in the naming: fields pointing to v4l2_device should be called v4l2_dev. There are too many device-like entities without adding to the confusion by mixing naming conventions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 2 +- drivers/media/video/v4l2-device.c | 12 ++++++------ include/media/v4l2-subdev.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index e1620e2a38ff..accc376e93cc 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -268,7 +268,7 @@ errors (except -ENOIOCTLCMD) occured, then 0 is returned. The second argument to both calls is a group ID. If 0, then all subdevs are called. If non-zero, then only those whose group ID match that value will -be called. Before a bridge driver registers a subdev it can set subdev->grp_id +be called. Before a bridge driver registers a subdev it can set sd->grp_id to whatever value it wants (it's 0 by default). This value is owned by the bridge driver and the sub-device driver will never modify or use it. diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 3330ffb7d010..b3dcb8454379 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -72,10 +72,10 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) return -EINVAL; /* Warn if we apparently re-register a subdev */ - WARN_ON(sd->dev != NULL); + WARN_ON(sd->v4l2_dev != NULL); if (!try_module_get(sd->owner)) return -ENODEV; - sd->dev = v4l2_dev; + sd->v4l2_dev = v4l2_dev; spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); spin_unlock(&v4l2_dev->lock); @@ -86,12 +86,12 @@ EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) { /* return if it isn't registered */ - if (sd == NULL || sd->dev == NULL) + if (sd == NULL || sd->v4l2_dev == NULL) return; - spin_lock(&sd->dev->lock); + spin_lock(&sd->v4l2_dev->lock); list_del(&sd->list); - spin_unlock(&sd->dev->lock); - sd->dev = NULL; + spin_unlock(&sd->v4l2_dev->lock); + sd->v4l2_dev = NULL; module_put(sd->owner); } EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index cd640c6f039b..05b69652e6c4 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -137,7 +137,7 @@ struct v4l2_subdev_ops { struct v4l2_subdev { struct list_head list; struct module *owner; - struct v4l2_device *dev; + struct v4l2_device *v4l2_dev; const struct v4l2_subdev_ops *ops; /* name must be unique */ char name[V4L2_SUBDEV_NAME_SIZE]; @@ -176,7 +176,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, /* ops->core MUST be set */ BUG_ON(!ops || !ops->core); sd->ops = ops; - sd->dev = NULL; + sd->v4l2_dev = NULL; sd->name[0] = '\0'; sd->grp_id = 0; sd->priv = NULL; -- cgit v1.2.3-59-g8ed1b From cc1139c7cdc1455fdf460c33fe63a36524753834 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 26 Feb 2009 23:08:22 -0300 Subject: V4L/DVB(10738a): remove include/linux/video_encoder.h include/linux/video_encoder.h is not used anymore by a v4l driver. Let's remove it and its occurences. Signed-off-by: Mauro Carvalho Chehab --- Documentation/ioctl/ioctl-number.txt | 1 - include/linux/Kbuild | 1 - include/linux/video_encoder.h | 23 ----------------------- 3 files changed, 25 deletions(-) delete mode 100644 include/linux/video_encoder.h (limited to 'Documentation') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index f1d639903325..3a311fe952ed 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -125,7 +125,6 @@ Code Seq# Include File Comments 'd' 00-DF linux/video_decoder.h conflict! 'd' F0-FF linux/digi1.h 'e' all linux/digi1.h conflict! -'e' 00-1F linux/video_encoder.h conflict! 'e' 00-1F net/irda/irtty.h conflict! 'f' 00-1F linux/ext2_fs.h 'h' 00-7F Charon filesystem diff --git a/include/linux/Kbuild b/include/linux/Kbuild index e9581fd9fb66..da7ff0ba3860 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -159,7 +159,6 @@ header-y += un.h header-y += utime.h header-y += veth.h header-y += video_decoder.h -header-y += video_encoder.h header-y += videotext.h header-y += x25.h diff --git a/include/linux/video_encoder.h b/include/linux/video_encoder.h deleted file mode 100644 index b7b6423bbb8a..000000000000 --- a/include/linux/video_encoder.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _LINUX_VIDEO_ENCODER_H -#define _LINUX_VIDEO_ENCODER_H - -#include - -struct video_encoder_capability { /* this name is too long */ - __u32 flags; -#define VIDEO_ENCODER_PAL 1 /* can encode PAL signal */ -#define VIDEO_ENCODER_NTSC 2 /* can encode NTSC */ -#define VIDEO_ENCODER_SECAM 4 /* can encode SECAM */ -#define VIDEO_ENCODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */ - int inputs; /* number of inputs */ - int outputs; /* number of outputs */ -}; - -#define ENCODER_GET_CAPABILITIES _IOR('e', 1, struct video_encoder_capability) -#define ENCODER_SET_NORM _IOW('e', 2, int) -#define ENCODER_SET_INPUT _IOW('e', 3, int) /* 0 <= input < #inputs */ -#define ENCODER_SET_OUTPUT _IOW('e', 4, int) /* 0 <= output < #outputs */ -#define ENCODER_ENABLE_OUTPUT _IOW('e', 5, int) /* boolean output enable control */ - - -#endif -- cgit v1.2.3-59-g8ed1b From 812c582390f2c6b81c0400d1286a7bce39d161d0 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 25 Feb 2009 16:52:31 -0300 Subject: V4L/DVB (10739): em28xx-cards: remove incorrect entry Removed EM2821_BOARD_PROLINK_PLAYTV_USB2 entry. This entry has a incorrect tuner set. Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 1 - drivers/media/video/em28xx/em28xx-cards.c | 20 -------------------- drivers/media/video/em28xx/em28xx.h | 1 - 3 files changed, 22 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 77874bd20550..dafbd955ee1b 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -30,7 +30,6 @@ 30 -> Videology 20K14XUSB USB2.0 (em2820/em2840) 31 -> Usbgear VD204v9 (em2821) 32 -> Supercomp USB 2.0 TV (em2821) - 33 -> SIIG AVTuner-PVR/Prolink PlayTV USB 2.0 (em2821) 34 -> Terratec Cinergy A Hybrid XS (em2860) [0ccd:004f] 35 -> Typhoon DVD Maker (em2860) 36 -> NetGMBH Cam (em2860) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 2b27460dae35..e263ab0f4ee4 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -369,26 +369,6 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_VIDEO, } }, }, - [EM2821_BOARD_PROLINK_PLAYTV_USB2] = { - .name = "SIIG AVTuner-PVR/Prolink PlayTV USB 2.0", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */ - .tda9887_conf = TDA9887_PRESENT, /* unknown? */ - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, [EM2821_BOARD_SUPERCOMP_USB_2] = { .name = "Supercomp USB 2.0 TV", .valid = EM28XX_BOARD_NOT_VALIDATED, diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 89a793cb8ca4..57a4084f9b5e 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -70,7 +70,6 @@ #define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 #define EM2821_BOARD_USBGEAR_VD204 31 #define EM2821_BOARD_SUPERCOMP_USB_2 32 -#define EM2821_BOARD_PROLINK_PLAYTV_USB2 33 #define EM2860_BOARD_TERRATEC_HYBRID_XS 34 #define EM2860_BOARD_TYPHOON_DVD_MAKER 35 #define EM2860_BOARD_NETGMBH_CAM 36 -- cgit v1.2.3-59-g8ed1b From 1f372a930c85270b4435b508b3e029021b1c5b62 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 25 Feb 2009 16:54:31 -0300 Subject: V4L/DVB (10740): em28xx-cards: Add SIIG AVTuner-PVR board Added SIIG AVTuner-PVR to the right entry. Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 2 +- drivers/media/video/em28xx/em28xx-cards.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index dafbd955ee1b..5707c6fbf89f 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -12,7 +12,7 @@ 11 -> Terratec Hybrid XS (em2880) [0ccd:0042] 12 -> Kworld PVR TV 2800 RF (em2820/em2840) 13 -> Terratec Prodigy XS (em2880) [0ccd:0047] - 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) + 14 -> SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) 15 -> V-Gear PocketTV (em2800) 16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b] 17 -> Pinnacle PCTV HD Pro Stick (em2880) [2304:0227] diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index e263ab0f4ee4..226dc154aaf0 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -951,7 +951,7 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { - .name = "Pixelview Prolink PlayTV USB 2.0", + .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0", .has_snapshot_button = 1, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_YMEC_TVF_5533MF, -- cgit v1.2.3-59-g8ed1b From ac40d9e09825c62b77e8b11b3ed201f390550351 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 25 Feb 2009 16:55:48 -0300 Subject: V4L/DVB (10741): em28xx: Add Kaiser Baas Video to DVD maker support Added usb vendor/product id for Kaiser Baas Video to DVD maker. Thanks to Trevor Campbell for providing all data and tests needed to add this card to em28xx driver. Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 2 +- drivers/media/video/em28xx/em28xx-cards.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 5707c6fbf89f..78d0a6eed571 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -7,7 +7,7 @@ 6 -> Terratec Cinergy 200 USB (em2800) 7 -> Leadtek Winfast USB II (em2800) [0413:6023] 8 -> Kworld USB2800 (em2800) - 9 -> Pinnacle Dazzle DVC 90/DVC 100/DVC 101/DVC 107 (em2820/em2840) [2304:0207,2304:021a] + 9 -> Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker (em2820/em2840) [1b80:e302,2304:0207,2304:021a] 10 -> Hauppauge WinTV HVR 900 (em2880) [2040:6500] 11 -> Terratec Hybrid XS (em2880) [0ccd:0042] 12 -> Kworld PVR TV 2800 RF (em2820/em2840) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 226dc154aaf0..f7c817765752 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -896,7 +896,7 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/DVC 100/DVC 101/DVC 107", + .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker", .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { @@ -1345,6 +1345,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2800_BOARD_GRABBEEX_USB2800 }, { USB_DEVICE(0xeb1a, 0xe357), .driver_info = EM2870_BOARD_KWORLD_355U }, + { USB_DEVICE(0x1b80, 0xe302), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, { USB_DEVICE(0x0ccd, 0x004c), -- cgit v1.2.3-59-g8ed1b From 5a23b0762c9095e137ce9a559cc7c37b2f8fd083 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 3 Mar 2009 12:06:09 -0300 Subject: V4L/DVB (10805): Add support for NetUP Dual DVB-S2 CI card Add support for NetUP Dual DVB-S2 CI card The card based on cx23885 PCI-e bridge, CiMax SP2 Common Interface chips, STM lnbh24 LNB power chip, stv6110 tuners and stv0900 demodulator. http://www.linuxtv.org/wiki/index.php/NetUP_Dual_DVB_S2_CI Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx23885 | 1 + drivers/media/video/cx23885/Kconfig | 1 + drivers/media/video/cx23885/Makefile | 4 +- drivers/media/video/cx23885/cx23885-cards.c | 53 ++++++++++++++ drivers/media/video/cx23885/cx23885-core.c | 20 +++++- drivers/media/video/cx23885/cx23885-dvb.c | 106 +++++++++++++++++++++++++++- drivers/media/video/cx23885/cx23885-reg.h | 2 + drivers/media/video/cx23885/cx23885.h | 3 + 8 files changed, 187 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 5937ff958f04..91aa3c0f0dd2 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -15,3 +15,4 @@ 14 -> TurboSight TBS 6920 [6920:8888] 15 -> TeVii S470 [d470:9022] 16 -> DVBWorld DVB-S2 2005 [0001:2005] + 17 -> NetUP Dual DVB-S2 CI [1b55:2a2c] diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index 00f1e2e8889e..b62f16d507d8 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -16,6 +16,7 @@ config VIDEO_CX23885 select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_TDA10048 if !DVB_FE_CUSTOMIZE + select DVB_LNBP21 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index 29c23b44c13c..ab8ea35c9bfb 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -1,4 +1,6 @@ -cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o +cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ + cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ + netup-init.o cimax2.o netup-eeprom.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 7ff339a2e3f2..08cd793cd151 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -27,6 +27,7 @@ #include "cx23885.h" #include "tuner-xc2028.h" +#include "netup-init.h" /* ------------------------------------------------------------------ */ /* board config info */ @@ -174,6 +175,12 @@ struct cx23885_board cx23885_boards[] = { .name = "DVBWorld DVB-S2 2005", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = { + .cimax = 1, + .name = "NetUP Dual DVB-S2 CI", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -269,6 +276,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0001, .subdevice = 0x2005, .card = CX23885_BOARD_DVBWORLD_2005, + }, { + .subvendor = 0x1b55, + .subdevice = 0x2a2c, + .card = CX23885_BOARD_NETUP_DUAL_DVBS2_CI, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -582,6 +593,32 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_write(MC417_OEN, 0x00001000); cx_write(MC417_RWD, 0x00001800); break; + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + /* GPIO-0 INTA from CiMax1 + GPIO-1 INTB from CiMax2 + GPIO-2 reset chips + GPIO-3 to GPIO-10 data/addr for CA + GPIO-11 ~CS0 to CiMax1 + GPIO-12 ~CS1 to CiMax2 + GPIO-13 ADL0 load LSB addr + GPIO-14 ADL1 load MSB addr + GPIO-15 ~RDY from CiMax + GPIO-17 ~RD to CiMax + GPIO-18 ~WR to CiMax + */ + cx_set(GP0_IO, 0x00040000); /* GPIO as out */ + /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */ + cx_clear(GP0_IO, 0x00030004); + mdelay(100);/* reset delay */ + cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */ + cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */ + /* GPIO-15 IN as ~ACK, rest as OUT */ + cx_write(MC417_OEN, 0x00001000); + /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */ + cx_write(MC417_RWD, 0x0000c300); + /* enable irq */ + cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ + break; } } @@ -669,6 +706,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: @@ -693,9 +738,17 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: request_module("cx25840"); break; } + + /* AUX-PLL 27MHz CLK */ + switch (dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + netup_initialize(dev); + break; + } } /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 8f6fb2add7de..1b401457d42e 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -31,6 +31,7 @@ #include #include "cx23885.h" +#include "cimax2.h" MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth "); @@ -791,6 +792,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->pci_bus = dev->pci->bus->number; dev->pci_slot = PCI_SLOT(dev->pci->devfn); dev->pci_irqmask = 0x001f00; + if (cx23885_boards[dev->board].cimax > 0) + dev->pci_irqmask |= 0x01800000; /* for CiMaxes */ /* External Master 1 Bus */ dev->i2c_bus[0].nr = 0; @@ -1643,7 +1646,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) (pci_status & PCI_MSK_VID_B) || (pci_status & PCI_MSK_VID_A) || (pci_status & PCI_MSK_AUD_INT) || - (pci_status & PCI_MSK_AUD_EXT)) { + (pci_status & PCI_MSK_AUD_EXT) || + (pci_status & PCI_MSK_GPIO0) || + (pci_status & PCI_MSK_GPIO1)) { if (pci_status & PCI_MSK_RISC_RD) dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n", @@ -1685,8 +1690,19 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) dprintk(7, " (PCI_MSK_AUD_EXT 0x%08x)\n", PCI_MSK_AUD_EXT); + if (pci_status & PCI_MSK_GPIO0) + dprintk(7, " (PCI_MSK_GPIO0 0x%08x)\n", + PCI_MSK_GPIO0); + + if (pci_status & PCI_MSK_GPIO1) + dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n", + PCI_MSK_GPIO1); } + if ((pci_status & PCI_MSK_GPIO0) || (pci_status & PCI_MSK_GPIO1)) + /* handled += cx23885_irq_gpio(dev, pci_status); */ + handled += netup_ci_slot_status(dev, pci_status); + if (ts1_status) { if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) handled += cx23885_irq_ts(ts1, ts1_status); @@ -1759,6 +1775,8 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, } pci_set_drvdata(pci_dev, dev); + cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */ + return 0; fail_irq: diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 14a6540b826c..9a0bc6e84a95 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -30,6 +30,7 @@ #include "cx23885.h" #include +#include "dvb_ca_en50221.h" #include "s5h1409.h" #include "s5h1411.h" #include "mt2131.h" @@ -43,7 +44,13 @@ #include "dib7000p.h" #include "dibx000_common.h" #include "zl10353.h" +#include "stv0900.h" +#include "stv6110.h" +#include "lnbh24.h" #include "cx24116.h" +#include "cimax2.h" +#include "netup-eeprom.h" +#include "netup-init.h" static unsigned int debug; @@ -309,6 +316,31 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = { .no_tuner = 1, }; +static struct stv0900_config netup_stv0900_config = { + .demod_address = 0x68, + .xtal = 27000000, + .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ + .diseqc_mode = 2,/* 2/3 PWM */ + .path1_mode = 2,/*Serial continues clock */ + .path2_mode = 2,/*Serial continues clock */ + .tun1_maddress = 0,/* 0x60 */ + .tun2_maddress = 3,/* 0x63 */ + .tun1_adc = 1,/* 1 Vpp */ + .tun2_adc = 1,/* 1 Vpp */ +}; + +static struct stv6110_config netup_stv6110_tunerconfig_a = { + .i2c_address = 0x60, + .mclk = 27000000, + .iq_wiring = 0, +}; + +static struct stv6110_config netup_stv6110_tunerconfig_b = { + .i2c_address = 0x63, + .mclk = 27000000, + .iq_wiring = 1, +}; + static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct cx23885_tsport *port = fe->dvb->priv; @@ -340,6 +372,7 @@ static int dvb_register(struct cx23885_tsport *port) struct cx23885_dev *dev = port->dev; struct cx23885_i2c *i2c_bus = NULL; struct videobuf_dvb_frontend *fe0; + int ret; /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1); @@ -580,6 +613,51 @@ static int dvb_register(struct cx23885_tsport *port) &dvbworld_cx24116_config, &i2c_bus->i2c_adap); break; + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + i2c_bus = &dev->i2c_bus[0]; + switch (port->nr) { + /* port B */ + case 1: + fe0->dvb.frontend = dvb_attach(stv0900_attach, + &netup_stv0900_config, + &i2c_bus->i2c_adap, 0); + if (fe0->dvb.frontend != NULL) { + if (dvb_attach(stv6110_attach, + fe0->dvb.frontend, + &netup_stv6110_tunerconfig_a, + &i2c_bus->i2c_adap)) { + if (!dvb_attach(lnbh24_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + LNBH24_PCL, 0, 0x09)) + printk(KERN_ERR + "No LNBH24 found!\n"); + + } + } + break; + /* port C */ + case 2: + fe0->dvb.frontend = dvb_attach(stv0900_attach, + &netup_stv0900_config, + &i2c_bus->i2c_adap, 1); + if (fe0->dvb.frontend != NULL) { + if (dvb_attach(stv6110_attach, + fe0->dvb.frontend, + &netup_stv6110_tunerconfig_b, + &i2c_bus->i2c_adap)) { + if (!dvb_attach(lnbh24_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + LNBH24_PCL, 0, 0x0a)) + printk(KERN_ERR + "No LNBH24 found!\n"); + + } + } + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", @@ -601,9 +679,33 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); /* register everything */ - return videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, + ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, &dev->pci->dev, adapter_nr, 0); + /* init CI & MAC */ + switch (dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: { + static struct netup_card_info cinfo; + + netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo); + memcpy(port->frontends.adapter.proposed_mac, + cinfo.port[port->nr - 1].mac, 6); + printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=" + "%02X:%02X:%02X:%02X:%02X:%02X\n", + port->nr, + port->frontends.adapter.proposed_mac[0], + port->frontends.adapter.proposed_mac[1], + port->frontends.adapter.proposed_mac[2], + port->frontends.adapter.proposed_mac[3], + port->frontends.adapter.proposed_mac[4], + port->frontends.adapter.proposed_mac[5]); + + netup_ci_init(port); + break; + } + } + + return ret; } int cx23885_dvb_register(struct cx23885_tsport *port) @@ -676,6 +778,8 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port) if (fe0->dvb.frontend) videobuf_dvb_unregister_bus(&port->frontends); + netup_ci_exit(port); + return 0; } diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h index 20b68a236260..eafbe5226bae 100644 --- a/drivers/media/video/cx23885/cx23885-reg.h +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -212,6 +212,8 @@ Channel manager Data Structure entry = 20 DWORD #define DEV_CNTRL2 0x00040000 +#define PCI_MSK_GPIO1 (1 << 24) +#define PCI_MSK_GPIO0 (1 << 23) #define PCI_MSK_APB_DMA (1 << 12) #define PCI_MSK_AL_WR (1 << 11) #define PCI_MSK_AL_RD (1 << 10) diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 37a88b1683c3..779fc35b18d6 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -70,6 +70,7 @@ #define CX23885_BOARD_TBS_6920 14 #define CX23885_BOARD_TEVII_S470 15 #define CX23885_BOARD_DVBWORLD_2005 16 +#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI 17 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ @@ -187,6 +188,7 @@ struct cx23885_board { */ u32 clk_freq; struct cx23885_input input[MAX_CX23885_INPUT]; + int cimax; /* for NetUP */ }; struct cx23885_subid { @@ -269,6 +271,7 @@ struct cx23885_tsport { /* Allow a single tsport to have multiple frontends */ u32 num_frontends; + void *port_priv; }; struct cx23885_dev { -- cgit v1.2.3-59-g8ed1b From 0c5db425519487d06a5a14eb369268f4a2b32677 Mon Sep 17 00:00:00 2001 From: Bruno Christo Date: Mon, 2 Mar 2009 22:38:59 -0300 Subject: V4L/DVB (10827): Add support for GeoVision GV-800(S) I have a GeoVision GV-800(S) card, it has 4 CONEXANT BT878A chips. It has 16 video inputs and 4 audio inputs, and it is almost identical to the GV-800, as seen on http://bttv-gallery.de . The only difference appears to be the analog mux, it has a CD22M3494 in place of the MT8816AP. The card has a blue PCB, as seen in this picture: http://www.gsbr.com.br/imagem/kits/GeoVision%20GV%20800.jpg . This card wasn't originally supported, and it was detected as UNKNOWN/GENERIC. The video inputs weren't working, so I tried "forcing" a few cards like the GeoVision GV-600, but there was still no video. So I made a patch to support this card, based on the Kodicom 4400r. The GV-800(S) is identified as follows: ... 02:00.0 Multimedia video controller: Brooktree Corporation Bt878 Video Capture (rev 11) 02:00.1 Multimedia controller: Brooktree Corporation Bt878 Audio Capture (rev 11) 02:04.0 Multimedia video controller: Brooktree Corporation Bt878 Video Capture (rev 11) 02:04.1 Multimedia controller: Brooktree Corporation Bt878 Audio Capture (rev 11) 02:08.0 Multimedia video controller: Brooktree Corporation Bt878 Video Capture (rev 11) 02:08.1 Multimedia controller: Brooktree Corporation Bt878 Audio Capture (rev 11) 02:0c.0 Multimedia video controller: Brooktree Corporation Bt878 Video Capture (rev 11) 02:0c.1 Multimedia controller: Brooktree Corporation Bt878 Audio Capture (rev 11) ... 02:00.0 0400: 109e:036e (rev 11) Subsystem: 800a:763d Flags: bus master, medium devsel, latency 32, IRQ 10 Memory at cdfff000 (32-bit, prefetchable) [size=4K] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 Kernel modules: bttv 02:00.1 0480: 109e:0878 (rev 11) Subsystem: 800a:763d Flags: bus master, medium devsel, latency 32, IRQ 10 Memory at cdffe000 (32-bit, prefetchable) [size=4K] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 02:04.0 0400: 109e:036e (rev 11) Subsystem: 800b:763d Flags: bus master, medium devsel, latency 32, IRQ 10 Memory at cdffd000 (32-bit, prefetchable) [size=4K] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 Kernel modules: bttv 02:04.1 0480: 109e:0878 (rev 11) Subsystem: 800b:763d Flags: bus master, medium devsel, latency 32, IRQ 10 Memory at cdffc000 (32-bit, prefetchable) [size=4K] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 02:08.0 0400: 109e:036e (rev 11) Subsystem: 800c:763d Flags: bus master, medium devsel, latency 32, IRQ 10 Memory at cdffb000 (32-bit, prefetchable) [size=4K] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 Kernel modules: bttv 02:08.1 0480: 109e:0878 (rev 11) Subsystem: 800c:763d Flags: bus master, medium devsel, latency 32, IRQ 10 Memory at cdffa000 (32-bit, prefetchable) [size=4K] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 02:0c.0 0400: 109e:036e (rev 11) Subsystem: 800d:763d Flags: bus master, medium devsel, latency 32, IRQ 10 Memory at cdff9000 (32-bit, prefetchable) [size=4K] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 Kernel modules: bttv 02:0c.1 0480: 109e:0878 (rev 11) Subsystem: 800d:763d Flags: bus master, medium devsel, latency 32, IRQ 10 Memory at cdff8000 (32-bit, prefetchable) [size=4K] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 As you can see, the GV-800(S) card is almost identical to the GV-800 on bttv-gallery, so this patch might also work for that card. If not, only a few changes should be required on the gv800s_write() function. After this patch, the video inputs work correctly on linux 2.6.24 and 2.6.27 using the software 'motion'. The input order may seem a little odd, but it's the order the original software/driver uses, and I decided to keep that order to get the most out of the card. I tried to get the audio working with the snd-bt87x module, but I only get noise from every audio input, even after selecting a different mux with alsamixer. Also, after trying to play sound from those sources, I randomly get a RISC error about an invalid RISC opcode, and then that output stops working. I also can't change the sampling rate when recording. Any pointers to adding audio support are welcome. Signed-off-by: Bruno Christo Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.bttv | 2 + drivers/media/video/bt8xx/bttv-cards.c | 181 +++++++++++++++++++++++++++++++- drivers/media/video/bt8xx/bttv.h | 2 + 3 files changed, 184 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index 4dfe62641374..1da2c62715ae 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -155,3 +155,5 @@ 154 -> PHYTEC VD-012-X1 (bt878) 155 -> PHYTEC VD-012-X2 (bt878) 156 -> IVCE-8784 [0000:f050,0001:f050,0002:f050,0003:f050] +157 -> Geovision GV-800(S) (master) [800a:763d] +158 -> Geovision GV-800(S) (slave) [800b:763d,800c:763d,800d:763d] diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index fd1ab7a15cd4..fbeb396dc1c5 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -75,6 +75,9 @@ static void geovision_muxsel(struct bttv *btv, unsigned int input); static void phytec_muxsel(struct bttv *btv, unsigned int input); +static void gv800s_muxsel(struct bttv *btv, unsigned int input); +static void gv800s_init(struct bttv *btv); + static int terratec_active_radio_upgrade(struct bttv *btv); static int tea5757_read(struct bttv *btv); static int tea5757_write(struct bttv *btv, int value); @@ -311,6 +314,10 @@ static struct CARD { { 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" }, { 0x763c008a, BTTV_BOARD_GEOVISION_GV600, "GeoVision GV-600" }, { 0x18011000, BTTV_BOARD_ENLTV_FM_2, "Encore ENL TV-FM-2" }, + { 0x763d800a, BTTV_BOARD_GEOVISION_GV800S, "GeoVision GV-800(S) (master)" }, + { 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, + { 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, + { 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, { 0, -1, NULL } }; @@ -2818,7 +2825,60 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - } + }, + [BTTV_BOARD_GEOVISION_GV800S] = { + /* Bruno Christo + * + * GeoVision GV-800(S) has 4 Conexant Fusion 878A: + * 1 audio input per BT878A = 4 audio inputs + * 4 video inputs per BT878A = 16 video inputs + * This is the first BT878A chip of the GV-800(S). It's the + * "master" chip and it controls the video inputs through an + * analog multiplexer (a CD22M3494) via some GPIO pins. The + * slaves should use card type 0x9e (following this one). + * There is a EEPROM on the card which is currently not handled. + * The audio input is not working yet. + */ + .name = "Geovision GV-800(S) (master)", + .video_inputs = 4, + /* .audio_inputs= 1, */ + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, + .svhs = NO_SVHS, + .gpiomask = 0xf107f, + .no_gpioirq = 1, + .muxsel = MUXSEL(2, 2, 2, 2), + .pll = PLL_28, + .no_msp34xx = 1, + .no_tda7432 = 1, + .no_tda9875 = 1, + .muxsel_hook = gv800s_muxsel, + }, + [BTTV_BOARD_GEOVISION_GV800S_SL] = { + /* Bruno Christo + * + * GeoVision GV-800(S) has 4 Conexant Fusion 878A: + * 1 audio input per BT878A = 4 audio inputs + * 4 video inputs per BT878A = 16 video inputs + * The 3 other BT878A chips are "slave" chips of the GV-800(S) + * and should use this card type. + * The audio input is not working yet. + */ + .name = "Geovision GV-800(S) (slave)", + .video_inputs = 4, + /* .audio_inputs= 1, */ + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, + .svhs = NO_SVHS, + .gpiomask = 0x00, + .no_gpioirq = 1, + .muxsel = MUXSEL(2, 2, 2, 2), + .pll = PLL_28, + .no_msp34xx = 1, + .no_tda7432 = 1, + .no_tda9875 = 1, + .muxsel_hook = gv800s_muxsel, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -3338,6 +3398,9 @@ void __devinit bttv_init_card2(struct bttv *btv) case BTTV_BOARD_KODICOM_4400R: kodicom4400r_init(btv); break; + case BTTV_BOARD_GEOVISION_GV800S: + gv800s_init(btv); + break; } /* pll configuration */ @@ -4544,6 +4607,122 @@ static void phytec_muxsel(struct bttv *btv, unsigned int input) gpio_bits(0x3, mux); } +/* + * GeoVision GV-800(S) functions + * Bruno Christo +*/ + +/* This is a function to control the analog switch, which determines which + * camera is routed to which controller. The switch comprises an X-address + * (gpio bits 0-3, representing the camera, ranging from 0-15), and a + * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3). + * A data value (gpio bit 18) of '1' enables the switch, and '0' disables + * the switch. A STROBE bit (gpio bit 17) latches the data value into the + * specified address. There is also a chip select (gpio bit 16). + * The idea is to set the address and chip select together, bring + * STROBE high, write the data, and finally bring STROBE back to low. + */ +static void gv800s_write(struct bttv *btv, + unsigned char xaddr, + unsigned char yaddr, + unsigned char data) { + /* On the "master" 878A: + * GPIO bits 0-9 are used for the analog switch: + * 00 - 03: camera selector + * 04 - 06: 878A (controller) selector + * 16: cselect + * 17: strobe + * 18: data (1->on, 0->off) + * 19: reset + */ + const u32 ADDRESS = ((xaddr&0xf) | (yaddr&3)<<4); + const u32 CSELECT = 1<<16; + const u32 STROBE = 1<<17; + const u32 DATA = data<<18; + + gpio_bits(0x1007f, ADDRESS | CSELECT); /* write ADDRESS and CSELECT */ + gpio_bits(0x20000, STROBE); /* STROBE high */ + gpio_bits(0x40000, DATA); /* write DATA */ + gpio_bits(0x20000, ~STROBE); /* STROBE low */ +} + +/* + * GeoVision GV-800(S) muxsel + * + * Each of the 4 cards (controllers) use this function. + * The controller using this function selects the input through the GPIO pins + * of the "master" card. A pointer to this card is stored in master[btv->c.nr]. + * + * The parameter 'input' is the requested camera number (0-4) on the controller. + * The map array has the address of each input. Note that the addresses in the + * array are in the sequence the original GeoVision driver uses, that is, set + * every controller to input 0, then to input 1, 2, 3, repeat. This means that + * the physical "camera 1" connector corresponds to controller 0 input 0, + * "camera 2" corresponds to controller 1 input 0, and so on. + * + * After getting the input address, the function then writes the appropriate + * data to the analog switch, and housekeeps the local copy of the switch + * information. + */ +static void gv800s_muxsel(struct bttv *btv, unsigned int input) +{ + struct bttv *mctlr; + char *sw_status; + int xaddr, yaddr; + static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 }, + { 0x1, 0x5, 0xb, 0x7 }, + { 0x2, 0x8, 0xc, 0xe }, + { 0x3, 0x9, 0xd, 0xf } }; + input = input%4; + mctlr = master[btv->c.nr]; + if (mctlr == NULL) { + /* do nothing until the "master" is detected */ + return; + } + yaddr = (btv->c.nr - mctlr->c.nr) & 3; + sw_status = (char *)(&mctlr->mbox_we); + xaddr = map[yaddr][input] & 0xf; + + /* Check if the controller/camera pair has changed, ignore otherwise */ + if (sw_status[yaddr] != xaddr) { + /* disable the old switch, enable the new one and save status */ + gv800s_write(mctlr, sw_status[yaddr], yaddr, 0); + sw_status[yaddr] = xaddr; + gv800s_write(mctlr, xaddr, yaddr, 1); + } +} + +/* GeoVision GV-800(S) "master" chip init */ +static void gv800s_init(struct bttv *btv) +{ + char *sw_status = (char *)(&btv->mbox_we); + int ix; + + gpio_inout(0xf107f, 0xf107f); + gpio_write(1<<19); /* reset the analog MUX */ + gpio_write(0); + + /* Preset camera 0 to the 4 controllers */ + for (ix = 0; ix < 4; ix++) { + sw_status[ix] = ix; + gv800s_write(btv, ix, ix, 1); + } + + /* Inputs on the "master" controller need this brightness fix */ + bttv_I2CWrite(btv, 0x18, 0x5, 0x90, 1); + + if (btv->c.nr > BTTV_MAX-4) + return; + /* + * Store the "master" controller pointer in the master + * array for later use in the muxsel function. + */ + master[btv->c.nr] = btv; + master[btv->c.nr+1] = btv; + master[btv->c.nr+2] = btv; + master[btv->c.nr+3] = btv; +} + /* ----------------------------------------------------------------------- */ /* motherboard chipset specific stuff */ diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index ead6e749372a..737a464606a9 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -181,6 +181,8 @@ #define BTTV_BOARD_VD012_X1 0x9a #define BTTV_BOARD_VD012_X2 0x9b #define BTTV_BOARD_IVCE8784 0x9c +#define BTTV_BOARD_GEOVISION_GV800S 0x9d +#define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e /* more card-specific defines */ -- cgit v1.2.3-59-g8ed1b From f9996c95623d63de6f5957512976137bbac729f0 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 28 Feb 2009 17:45:17 -0300 Subject: V4L/DVB (10877): saa7134: add analog support for Hauppauge HVR1110r3 boards Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 2 + drivers/media/video/saa7134/saa7134-cards.c | 173 +++++++++++++++++++++++++++- drivers/media/video/saa7134/saa7134.h | 2 + 3 files changed, 175 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index b8d470596b0c..325c69fe91fd 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -153,3 +153,5 @@ 152 -> Asus Tiger Rev:1.00 [1043:4857] 153 -> Kworld Plus TV Analog Lite PCI [17de:7128] 154 -> Avermedia AVerTV GO 007 FM Plus [1461:f31d] +155 -> Hauppauge WinTV-HVR1150 [0070:6706,0070:6708] +156 -> Hauppauge WinTV-HVR1110r3 [0070:6707,0070:6709,0070:670a] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 9f69c7c85814..88f0b8f06e10 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -31,6 +31,7 @@ #include #include #include "tea5767.h" +#include "tda18271.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -3291,6 +3292,66 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200100, }, }, + [SAA7134_BOARD_HAUPPAUGE_HVR1150] = { + .name = "Hauppauge WinTV-HVR1150", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 3, + .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0800100, /* GPIO 23 HI for FM */ + }, + }, + [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = { + .name = "Hauppauge WinTV-HVR1110r3", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 3, + .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0800100, /* GPIO 23 HI for FM */ + }, + }, [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { .name = "Terratec Cinergy HT PCMCIA", .audio_clock = 0x00187de7, @@ -5400,6 +5461,36 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x0070, .subdevice = 0x6705, .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6706, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6707, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6708, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6709, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x670a, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -5819,8 +5910,8 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, } -static int saa7134_tda8290_callback(struct saa7134_dev *dev, - int command, int arg) +static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev, + int command, int arg) { u8 sync_control; @@ -5846,6 +5937,65 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, return 0; } +static inline int saa7134_tda18271_hvr11x0_toggle_agc(struct saa7134_dev *dev, + enum tda18271_mode mode) +{ + /* toggle AGC switch through GPIO 26 */ + switch (mode) { + case TDA18271_ANALOG: + saa7134_set_gpio(dev, 26, 0); + break; + case TDA18271_DIGITAL: + saa7134_set_gpio(dev, 26, 1); + break; + default: + return -EINVAL; + } + return 0; +} + +static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, + int command, int arg) +{ + int ret = 0; + + switch (command) { + case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ + switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); + break; + default: + break; + } + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int saa7134_tda8290_callback(struct saa7134_dev *dev, + int command, int arg) +{ + int ret; + + switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + /* tda8290 + tda18271 */ + ret = saa7134_tda8290_18271_callback(dev, command, arg); + break; + default: + /* tda8290 + tda827x */ + ret = saa7134_tda8290_827x_callback(dev, command, arg); + break; + } + return ret; +} + int saa7134_tuner_callback(void *priv, int component, int command, int arg) { struct saa7134_dev *dev = priv; @@ -5876,11 +6026,16 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) switch (tv.model) { case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ + case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ + case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ case 67569: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM) */ case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ + case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ break; default: printk(KERN_WARNING "%s: warning: " @@ -6057,6 +6212,16 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + /* GPIO 26 high for digital, low for analog */ + saa7134_set_gpio(dev, 26, 0); + msleep(1); + + saa7134_set_gpio(dev, 22, 0); + msleep(10); + saa7134_set_gpio(dev, 22, 1); + break; /* i2c remotes */ case SAA7134_BOARD_PINNACLE_PCTV_110i: case SAA7134_BOARD_PINNACLE_PCTV_310i: @@ -6309,6 +6474,10 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); } break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + hauppauge_eeprom(dev, dev->eedata+0x80); + break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: hauppauge_eeprom(dev, dev->eedata+0x80); /* break intentionally omitted */ diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 4552a4d6f192..52d9397ad779 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -278,6 +278,8 @@ struct saa7134_format { #define SAA7134_BOARD_ASUSTeK_TIGER 152 #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 +#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155 +#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 -- cgit v1.2.3-59-g8ed1b From 42d12f5aa105af08bc0ed0580e32156a1a325c6b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 10 Mar 2009 05:02:28 -0300 Subject: V4L/DVB (10870a): remove all references for video_decoder.h changeset 04934e44e3784a1b969582e2d59afcec278470c6 removed the last implementation that were still using the V4L1 obsoleted header. Now, video_decoder.h is not used anymore by any driver. Let's remove it and all references for it in Kernel. Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 8 ++--- Documentation/ioctl/ioctl-number.txt | 1 - drivers/media/video/mxb.c | 1 - include/linux/Kbuild | 1 - include/linux/video_decoder.h | 48 ------------------------------ 5 files changed, 4 insertions(+), 55 deletions(-) delete mode 100644 include/linux/video_decoder.h (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 1135996bec8b..5e02b83ac12b 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -64,10 +64,10 @@ Who: Pavel Machek --------------------------- -What: Video4Linux API 1 ioctls and video_decoder.h from Video devices. -When: December 2008 -Files: include/linux/video_decoder.h include/linux/videodev.h -Check: include/linux/video_decoder.h include/linux/videodev.h +What: Video4Linux API 1 ioctls and from Video devices. +When: July 2009 +Files: include/linux/videodev.h +Check: include/linux/videodev.h Why: V4L1 AP1 was replaced by V4L2 API during migration from 2.4 to 2.6 series. The old API have lots of drawbacks and don't provide enough means to work with all video and audio standards. The newer API is diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 3a311fe952ed..1f779a25c703 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -122,7 +122,6 @@ Code Seq# Include File Comments 'c' 00-7F linux/coda.h conflict! 'c' 80-9F arch/s390/include/asm/chsc.h 'd' 00-FF linux/char/drm/drm/h conflict! -'d' 00-DF linux/video_decoder.h conflict! 'd' F0-FF linux/digi1.h 'e' all linux/digi1.h conflict! 'e' 00-1F net/irda/irtty.h conflict! diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 996011f2aba5..84aec62e8452 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -25,7 +25,6 @@ #include #include -#include #include #include diff --git a/include/linux/Kbuild b/include/linux/Kbuild index da7ff0ba3860..a67b6227d272 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -158,7 +158,6 @@ header-y += ultrasound.h header-y += un.h header-y += utime.h header-y += veth.h -header-y += video_decoder.h header-y += videotext.h header-y += x25.h diff --git a/include/linux/video_decoder.h b/include/linux/video_decoder.h deleted file mode 100644 index e26c0c86a6ea..000000000000 --- a/include/linux/video_decoder.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _LINUX_VIDEO_DECODER_H -#define _LINUX_VIDEO_DECODER_H - -#include - -#define HAVE_VIDEO_DECODER 1 - -struct video_decoder_capability { /* this name is too long */ - __u32 flags; -#define VIDEO_DECODER_PAL 1 /* can decode PAL signal */ -#define VIDEO_DECODER_NTSC 2 /* can decode NTSC */ -#define VIDEO_DECODER_SECAM 4 /* can decode SECAM */ -#define VIDEO_DECODER_AUTO 8 /* can autosense norm */ -#define VIDEO_DECODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */ - int inputs; /* number of inputs */ - int outputs; /* number of outputs */ -}; - -/* -DECODER_GET_STATUS returns the following flags. The only one you need is -DECODER_STATUS_GOOD, the others are just nice things to know. -*/ -#define DECODER_STATUS_GOOD 1 /* receiving acceptable input */ -#define DECODER_STATUS_COLOR 2 /* receiving color information */ -#define DECODER_STATUS_PAL 4 /* auto detected */ -#define DECODER_STATUS_NTSC 8 /* auto detected */ -#define DECODER_STATUS_SECAM 16 /* auto detected */ - -struct video_decoder_init { - unsigned char len; - const unsigned char *data; -}; - -#define DECODER_GET_CAPABILITIES _IOR('d', 1, struct video_decoder_capability) -#define DECODER_GET_STATUS _IOR('d', 2, int) -#define DECODER_SET_NORM _IOW('d', 3, int) -#define DECODER_SET_INPUT _IOW('d', 4, int) /* 0 <= input < #inputs */ -#define DECODER_SET_OUTPUT _IOW('d', 5, int) /* 0 <= output < #outputs */ -#define DECODER_ENABLE_OUTPUT _IOW('d', 6, int) /* boolean output enable control */ -#define DECODER_SET_PICTURE _IOW('d', 7, struct video_picture) -#define DECODER_SET_GPIO _IOW('d', 8, int) /* switch general purpose pin */ -#define DECODER_INIT _IOW('d', 9, struct video_decoder_init) /* init internal registers at once */ -#define DECODER_SET_VBI_BYPASS _IOW('d', 10, int) /* switch vbi bypass */ - -#define DECODER_DUMP _IO('d', 192) /* debug hook */ - - -#endif -- cgit v1.2.3-59-g8ed1b From cf5193c74009e33428daefbf74f4770733c8ee36 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 6 Mar 2009 09:29:09 -0300 Subject: V4L/DVB (10909): tvmixer: remove last remaining references to this deleted module. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/bttv/Insmod-options | 10 ---------- drivers/media/video/Makefile | 1 - 2 files changed, 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/bttv/Insmod-options b/Documentation/video4linux/bttv/Insmod-options index 5ef75787f83a..bbe3ed667d91 100644 --- a/Documentation/video4linux/bttv/Insmod-options +++ b/Documentation/video4linux/bttv/Insmod-options @@ -81,16 +81,6 @@ tuner.o pal=[bdgil] select PAL variant (used for some tuners only, important for the audio carrier). -tvmixer.o - registers a mixer device for the TV card's volume/bass/treble - controls (requires a i2c audio control chip like the msp3400). - - insmod args: - debug=1 print some debug info to the syslog. - devnr=n allocate device #n (0 == /dev/mixer, - 1 = /dev/mixer1, ...), default is to - use the first free one. - tvaudio.o new, experimental module which is supported to provide a single driver for all simple i2c audio control chips (tda/tea*). diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 307490ebc230..08a0675fea34 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o -obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o -- cgit v1.2.3-59-g8ed1b From e8944282a76f94bcf84ae3b7a91328bd03835ea1 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 10 Mar 2009 17:02:07 -0300 Subject: V4L/DVB (10923): saa7134: fix typo in product name replace occurances of "HVR1150" with "HVR1120" - this was a typo. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 2 +- drivers/media/video/saa7134/saa7134-cards.c | 20 ++++++++++---------- drivers/media/video/saa7134/saa7134.h | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 325c69fe91fd..626820e519e3 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -153,5 +153,5 @@ 152 -> Asus Tiger Rev:1.00 [1043:4857] 153 -> Kworld Plus TV Analog Lite PCI [17de:7128] 154 -> Avermedia AVerTV GO 007 FM Plus [1461:f31d] -155 -> Hauppauge WinTV-HVR1150 [0070:6706,0070:6708] +155 -> Hauppauge WinTV-HVR1120 [0070:6706,0070:6708] 156 -> Hauppauge WinTV-HVR1110r3 [0070:6707,0070:6709,0070:670a] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 88f0b8f06e10..be53d8f35425 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3292,8 +3292,8 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200100, }, }, - [SAA7134_BOARD_HAUPPAUGE_HVR1150] = { - .name = "Hauppauge WinTV-HVR1150", + [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { + .name = "Hauppauge WinTV-HVR1120", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -5466,7 +5466,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6706, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -5478,7 +5478,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6708, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -5962,7 +5962,7 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, switch (command) { case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ switch (dev->board) { - case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); break; @@ -5983,7 +5983,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, int ret; switch (dev->board) { - case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: /* tda8290 + tda18271 */ ret = saa7134_tda8290_18271_callback(dev, command, arg); @@ -6026,7 +6026,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) switch (tv.model) { case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ - case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ @@ -6034,7 +6034,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ - case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ break; default: @@ -6212,7 +6212,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); break; - case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: /* GPIO 26 high for digital, low for analog */ saa7134_set_gpio(dev, 26, 0); @@ -6474,7 +6474,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); } break; - case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: hauppauge_eeprom(dev, dev->eedata+0x80); break; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 52d9397ad779..f078de98ef57 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -278,7 +278,7 @@ struct saa7134_format { #define SAA7134_BOARD_ASUSTeK_TIGER 152 #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 -#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155 +#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155 #define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 #define SAA7134_MAXBOARDS 32 -- cgit v1.2.3-59-g8ed1b From 3abdedd8a4e3b1a0ad164c67929b3e798c85cd11 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 19 Jan 2009 01:10:49 -0300 Subject: V4L/DVB (10926): saa7134: enable digital tv support for Hauppauge WinTV-HVR1120 Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 2 +- drivers/media/video/saa7134/Kconfig | 3 ++ drivers/media/video/saa7134/saa7134-cards.c | 4 ++- drivers/media/video/saa7134/saa7134-dvb.c | 44 +++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 626820e519e3..6dacf2825259 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -153,5 +153,5 @@ 152 -> Asus Tiger Rev:1.00 [1043:4857] 153 -> Kworld Plus TV Analog Lite PCI [17de:7128] 154 -> Avermedia AVerTV GO 007 FM Plus [1461:f31d] -155 -> Hauppauge WinTV-HVR1120 [0070:6706,0070:6708] +155 -> Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid [0070:6706,0070:6708] 156 -> Hauppauge WinTV-HVR1110r3 [0070:6707,0070:6709,0070:670a] diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 51f17c82bc30..e62b2996768f 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -42,6 +42,9 @@ config VIDEO_SAA7134_DVB select DVB_MT312 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index be53d8f35425..265a52ff8c33 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3293,13 +3293,15 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { - .name = "Hauppauge WinTV-HVR1120", + .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tuner_config = 3, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_SERIAL, .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ .inputs = {{ .name = name_tv, diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 80ad96ad8939..4eff1ca8593c 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -48,6 +48,9 @@ #include "isl6405.h" #include "lnbp21.h" #include "tuner-simple.h" +#include "tda18271.h" +#include "lgdt3305.h" +#include "tda8290.h" #include "zl10353.h" @@ -964,6 +967,34 @@ static struct zl10036_config avertv_a700_tuner = { .tuner_address = 0x60, }; +static struct lgdt3305_config hcw_lgdt3305_config = { + .i2c_addr = 0x0e, + .mpeg_mode = LGDT3305_MPEG_SERIAL, + .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .deny_i2c_rptr = 1, + .spectral_inversion = 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x58, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x58, }, +}; + +static struct tda18271_config hcw_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .config = 3, +}; + +static struct tda829x_config tda829x_no_probe = { + .probe_tuner = TDA829X_DONT_PROBE, +}; + /* ================================================================== * Core code */ @@ -1090,6 +1121,19 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_1) < 0) goto dettach_frontend; break; + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + fe0->dvb.frontend = dvb_attach(lgdt3305_attach, + &hcw_lgdt3305_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &hcw_tda18271_config); + } + break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: if (configure_tda827x_fe(dev, &asus_p7131_dual_config, &tda827x_cfg_0) < 0) -- cgit v1.2.3-59-g8ed1b From 4dbf46a0485a5b0704e1c4b55a173128fbaedec9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 10 Mar 2009 23:28:17 -0300 Subject: V4L/DVB (10931): zoran: Drop the lock_norm module parameter The lock_norm module parameter doesn't look terribly useful. If you don't want to change the norm, just don't change it. As a matter of fact, no other v4l driver has such a parameter. Cc: Hans Verkuil Signed-off-by: Jean Delvare Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/Zoran | 3 +-- drivers/media/video/zoran/zoran_driver.c | 20 -------------------- 2 files changed, 1 insertion(+), 22 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/Zoran b/Documentation/video4linux/Zoran index 295462b2317a..0e89e7676298 100644 --- a/Documentation/video4linux/Zoran +++ b/Documentation/video4linux/Zoran @@ -401,8 +401,7 @@ Additional notes for software developers: first set the correct norm. Well, it seems logically correct: TV standard is "more constant" for current country than geometry settings of a variety of TV capture cards which may work in ITU or - square pixel format. Remember that users now can lock the norm to - avoid any ambiguity. + square pixel format. -- Please note that lavplay/lavrec are also included in the MJPEG-tools (http://mjpeg.sf.net/). diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 26be1a8908a3..1869d307a59d 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -162,10 +162,6 @@ const struct zoran_format zoran_formats[] = { }; #define NUM_FORMATS ARRAY_SIZE(zoran_formats) -static int lock_norm; /* 0 = default 1 = Don't change TV standard (norm) */ -module_param(lock_norm, int, 0644); -MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)"); - /* small helper function for calculating buffersizes for v4l2 * we calculate the nearest higher power-of-two, which * will be the recommended buffersize */ @@ -1483,22 +1479,6 @@ zoran_set_norm (struct zoran *zr, return -EBUSY; } - if (lock_norm && norm != zr->norm) { - if (lock_norm > 1) { - dprintk(1, - KERN_WARNING - "%s: set_norm() - TV standard is locked, can not switch norm\n", - ZR_DEVNAME(zr)); - return -EPERM; - } else { - dprintk(1, - KERN_WARNING - "%s: set_norm() - TV standard is locked, norm was not changed\n", - ZR_DEVNAME(zr)); - norm = zr->norm; - } - } - if (!(norm & zr->card.norms)) { dprintk(1, KERN_ERR "%s: set_norm() - unsupported norm %llx\n", -- cgit v1.2.3-59-g8ed1b From 76ecf4599e55fd16bdb333a737c6243105c916e6 Mon Sep 17 00:00:00 2001 From: Robert Millan Date: Wed, 11 Mar 2009 08:18:53 -0300 Subject: V4L/DVB (10944): Conceptronic CTVFMI2 PCI Id My BTTV_BOARD_CONCEPTRONIC_CTVFMI2 card wasn't auto-detected, here's a patch that adds its PCI id. lspci -nnv output: 05:06.0 Multimedia video controller [0400]: Brooktree Corporation Bt878 Video Capture [109e:036e] (rev 11) 05:06.1 Multimedia controller [0480]: Brooktree Corporation Bt878 Audio Capture [109e:0878] (rev 11) Press within 3 seconds if this is wrong. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.bttv | 2 +- drivers/media/video/bt8xx/bttv-cards.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index 1da2c62715ae..e17750473e08 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -135,7 +135,7 @@ 134 -> Adlink RTV24 135 -> DViCO FusionHDTV 5 Lite [18ac:d500] 136 -> Acorp Y878F [9511:1540] -137 -> Conceptronic CTVFMi v2 +137 -> Conceptronic CTVFMi v2 [036e:109e] 138 -> Prolink Pixelview PV-BT878P+ (Rev.2E) 139 -> Prolink PixelView PlayTV MPEG2 PV-M4900 140 -> Osprey 440 [0070:ff07] diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index fbeb396dc1c5..187ed4ef3993 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -298,6 +298,8 @@ static struct CARD { /* Duplicate PCI ID, reconfigure for this board during the eeprom read. * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */ + { 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2, "Conceptronic CTVFMi v2"}, + /* DVB cards (using pci function .1 for mpeg data xfer) */ { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" }, { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, -- cgit v1.2.3-59-g8ed1b From 70101a2785598f1a743c1e0fb65264c55bf5a29f Mon Sep 17 00:00:00 2001 From: Stephan Wienczny Date: Tue, 10 Mar 2009 19:08:06 -0300 Subject: V4L/DVB (10949): Add support for Terratec Cinergy HT PCI MKII This patch adds support for Terratec Cinergy HT PCI MKII with card id 79. Its more or less a copy of Pinnacle Hybrid PCTV. Thanks to k1ngf1sher on forum.ubuntuusers.de for the idea to copy that card. Signed-off-by: Stephan Wienczny Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx88 | 1 + drivers/media/video/cx88/cx88-cards.c | 38 +++++++++++++++++++++++++++++++++ drivers/media/video/cx88/cx88-dvb.c | 16 ++++++++++++++ drivers/media/video/cx88/cx88.h | 1 + 4 files changed, 56 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index 0d08f1edcf6d..71e9db0b26f7 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -77,3 +77,4 @@ 76 -> SATTRADE ST4200 DVB-S/S2 [b200:4200] 77 -> TBS 8910 DVB-S [8910:8888] 78 -> Prof 6200 DVB-S [b022:3022] + 79 -> Terratec Cinergy HT PCI MKII [153b:1177] diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 733ede34f93a..1d7e3a562995 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1934,6 +1934,39 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = { + .name = "Terratec Cinergy HT PCI MKII", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .radio_type = TUNER_XC2028, + .radio_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x00001, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x0ff, + }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -2343,6 +2376,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0xb200, .subdevice = 0x4200, .card = CX88_BOARD_SATTRADE_ST4200, + }, { + .subvendor = 0x153b, + .subdevice = 0x1177, + .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII, }, }; @@ -2819,6 +2856,7 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) */ break; case CX88_BOARD_PINNACLE_HYBRID_PCTV: + case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: ctl->demod = XC3028_FE_ZARLINK456; ctl->mts = 1; break; diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index aef5297534af..08346fa05cd7 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -241,6 +241,12 @@ static struct mt352_config dvico_fusionhdtv_dual = { .demod_init = dvico_dual_demod_init, }; +static struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, +}; + #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) { @@ -1131,6 +1137,16 @@ static int dvb_register(struct cx8802_dev *dev) if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; break; + case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &cx88_terratec_cinergy_ht_pci_mkii_config, + &core->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; + if (attach_xc3028(0x61, dev) < 0) + goto frontend_detach; + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", core->name); diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 3542061c7329..303d8d20fc91 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -231,6 +231,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_SATTRADE_ST4200 76 #define CX88_BOARD_TBS_8910 77 #define CX88_BOARD_PROF_6200 78 +#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v1.2.3-59-g8ed1b From 2c79252326421dd49c059aceec0880d2cf15b17a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Mar 2009 18:34:19 -0300 Subject: V4L/DVB (10980): doc: improve the v4l2-framework documentation. Emphasize the need to call i2c_set_adapdata and clarify the use of the chipid in v4l2_i2c_new_(probed_)device(). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index accc376e93cc..51a7b6db118f 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -359,8 +359,8 @@ This loads the given module (can be NULL if no module needs to be loaded) and calls i2c_new_device() with the given i2c_adapter and chip/address arguments. If all goes well, then it registers the subdev with the v4l2_device. It gets the v4l2_device by calling i2c_get_adapdata(adapter), so you should make sure -that adapdata is set to v4l2_device when you setup the i2c_adapter in your -driver. +to call i2c_set_adapdata(adapter, v4l2_device) when you setup the i2c_adapter +in your driver. You can also use v4l2_i2c_new_probed_subdev() which is very similar to v4l2_i2c_new_subdev(), except that it has an array of possible I2C addresses @@ -368,6 +368,14 @@ that it should probe. Internally it calls i2c_new_probed_device(). Both functions return NULL if something went wrong. +Note that the chipid you pass to v4l2_i2c_new_(probed_)subdev() is usually +the same as the module name. It allows you to specify a chip variant, e.g. +"saa7114" or "saa7115". In general though the i2c driver autodetects this. +The use of chipid is something that needs to be looked at more closely at a +later date. It differs between i2c drivers and as such can be confusing. +To see which chip variants are supported you can look in the i2c driver code +for the i2c_device_id table. This lists all the possibilities. + struct video_device ------------------- -- cgit v1.2.3-59-g8ed1b From 005759613b95264fba9138010f112bc138c857c2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Mar 2009 10:03:04 -0300 Subject: V4L/DVB (10988): v4l2-dev: use parent field if the v4l2_device has no parent set. Normally the parent device of v4l2_device is used as the video device node's parent. But if it was not set, then use the parent field in the video_device struct. This is needed in the cx88 driver, which has one core v4l2_device but creates multiple pci devices (one each for raw and mpeg video). So you cannot associate the core v4l2_device with a particular PCI device, but you can do that for each video_device. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 12 +++++++++++- drivers/media/video/v4l2-dev.c | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 51a7b6db118f..df0247ed13d8 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -91,7 +91,8 @@ NULL, then you *must* setup v4l2_dev->name before calling v4l2_device_register. The first 'dev' argument is normally the struct device pointer of a pci_dev, usb_device or platform_device. It is rare for dev to be NULL, but it happens -with ISA devices, for example. +with ISA devices or when one device creates multiple PCI devices, thus making +it impossible to associate v4l2_dev with a particular parent. You unregister with: @@ -414,6 +415,15 @@ You should also set these fields: - ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance (highly recommended to use this and it might become compulsory in the future!), then set this to your v4l2_ioctl_ops struct. +- parent: you only set this if v4l2_device was registered with NULL as + the parent device struct. This only happens in cases where one hardware + device has multiple PCI devices that all share the same v4l2_device core. + + The cx88 driver is an example of this: one core v4l2_device struct, but + it is used by both an raw video PCI device (cx8800) and a MPEG PCI device + (cx8802). Since the v4l2_device cannot be associated with a particular + PCI device it is setup without a parent device. But when the struct + video_device is setup you do know which parent PCI device to use. If you use v4l2_ioctl_ops, then you should set either .unlocked_ioctl or .ioctl to video_ioctl2 in your v4l2_file_operations struct. diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 64c638ecdd4b..cdc8ce3c4e56 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -395,7 +395,7 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, vdev->vfl_type = type; vdev->cdev = NULL; - if (vdev->v4l2_dev) + if (vdev->v4l2_dev && vdev->v4l2_dev->dev) vdev->parent = vdev->v4l2_dev->dev; /* Part 2: find a free minor, kernel number and device index. */ -- cgit v1.2.3-59-g8ed1b From 98ec633972a70cf71d71bc8762804f0af4792d08 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 8 Mar 2009 17:02:10 -0300 Subject: V4L/DVB (11021): v4l2-device: add a notify callback. Add a notify callback to v4l2_device to let sub-devices notify their parent of special events. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 10 ++++++++++ include/media/v4l2-device.h | 3 +++ include/media/v4l2-subdev.h | 5 +++++ 3 files changed, 18 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index df0247ed13d8..4207590b2ac8 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -94,6 +94,11 @@ usb_device or platform_device. It is rare for dev to be NULL, but it happens with ISA devices or when one device creates multiple PCI devices, thus making it impossible to associate v4l2_dev with a particular parent. +You can also supply a notify() callback that can be called by sub-devices to +notify you of events. Whether you need to set this depends on the sub-device. +Any notifications a sub-device supports must be defined in a header in +include/media/.h. + You unregister with: v4l2_device_unregister(struct v4l2_device *v4l2_dev); @@ -281,6 +286,11 @@ e.g. AUDIO_CONTROLLER and specify that as the group ID value when calling v4l2_device_call_all(). That ensures that it will only go to the subdev that needs it. +If the sub-device needs to notify its v4l2_device parent of an event, then +it can call v4l2_subdev_notify(sd, notification, arg). This macro checks +whether there is a notify() callback defined and returns -ENODEV if not. +Otherwise the result of the notify() call is returned. + The advantage of using v4l2_subdev is that it is a generic struct and does not contain any knowledge about the underlying hardware. So a driver might contain several subdevs that use an I2C bus, but also a subdev that is diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 5d7146dc2913..3d8e96f6ceb3 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -44,6 +44,9 @@ struct v4l2_device { spinlock_t lock; /* unique device name, by default the driver name + bus ID */ char name[V4L2_DEVICE_NAME_SIZE]; + /* notify callback called by some sub-devices. */ + void (*notify)(struct v4l2_subdev *sd, + unsigned int notification, void *arg); }; /* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev. diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 05b69652e6c4..1b97a2c33a73 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -191,4 +191,9 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \ (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD)) +/* Send a notification to v4l2_device. */ +#define v4l2_subdev_notify(sd, notification, arg) \ + ((!(sd) || !(sd)->v4l2_dev || !(sd)->v4l2_dev->notify) ? -ENODEV : \ + (sd)->v4l2_dev->notify((sd), (notification), (arg))) + #endif -- cgit v1.2.3-59-g8ed1b From 71cb2764fcc51bd9e1b95be5b0f2da6f026634c7 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 3 Mar 2009 05:33:41 -0300 Subject: V4L/DVB (11039): gspca - most jpeg subdrivers: Change the JPEG header creation. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.bttv | 1 + drivers/media/video/gspca/conex.c | 21 ++- drivers/media/video/gspca/jpeg.h | 250 ++++++-------------------------- drivers/media/video/gspca/mars.c | 23 ++- drivers/media/video/gspca/sonixj.c | 94 ++++++++---- drivers/media/video/gspca/spca500.c | 22 ++- drivers/media/video/gspca/stk014.c | 25 +++- drivers/media/video/gspca/sunplus.c | 22 ++- drivers/media/video/gspca/zc3xx.c | 18 ++- 9 files changed, 226 insertions(+), 250 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index e17750473e08..f11c583295e9 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -157,3 +157,4 @@ 156 -> IVCE-8784 [0000:f050,0001:f050,0002:f050,0003:f050] 157 -> Geovision GV-800(S) (master) [800a:763d] 158 -> Geovision GV-800(S) (slave) [800b:763d,800c:763d,800d:763d] +159 -> ProVideo PV183 [1830:1540,1831:1540,1832:1540,1833:1540,1834:1540,1835:1540,1836:1540,1837:1540] diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index de2e608bf5ba..fd4df402bc2f 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -23,7 +23,6 @@ #include "gspca.h" #define CONEX_CAM 1 /* special JPEG header */ -#define QUANT_VAL 0 /* quantization table */ #include "jpeg.h" MODULE_AUTHOR("Michel Xhaard "); @@ -37,6 +36,9 @@ struct sd { unsigned char brightness; unsigned char contrast; unsigned char colors; + u8 quality; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -820,6 +822,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->quality = 40; return 0; } @@ -836,6 +839,14 @@ static int sd_init(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + cx11646_initsize(gspca_dev); cx11646_fw(gspca_dev); cx_sensor(gspca_dev); @@ -846,8 +857,11 @@ static int sd_start(struct gspca_dev *gspca_dev) /* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int retry = 50; + kfree(sd->jpeg_hdr); + if (!gspca_dev->present) return; reg_w_val(gspca_dev, 0x0000, 0x00); @@ -873,6 +887,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ @@ -880,7 +896,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, 0); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); data += 2; len -= 2; } diff --git a/drivers/media/video/gspca/jpeg.h b/drivers/media/video/gspca/jpeg.h index 7d2df9720025..de63c36806c0 100644 --- a/drivers/media/video/gspca/jpeg.h +++ b/drivers/media/video/gspca/jpeg.h @@ -27,42 +27,16 @@ /* * generation options * CONEX_CAM Conexant if present - * QUANT_VAL quantization table (0..8) */ -/* - * JPEG header: - * - start of jpeg frame - * - quantization table - * - huffman table - * - start of SOF0 - */ +/* JPEG header */ static const u8 jpeg_head[] = { 0xff, 0xd8, /* jpeg */ + +/* quantization table quality 50% */ 0xff, 0xdb, 0x00, 0x84, /* DQT */ -#if QUANT_VAL == 0 -/* index 0 - Q40*/ -0, /* quantization table part 1 */ - 0x14, 0x0e, 0x0f, 0x12, 0x0f, 0x0d, 0x14, 0x12, - 0x10, 0x12, 0x17, 0x15, 0x14, 0x18, 0x1e, 0x32, - 0x21, 0x1e, 0x1c, 0x1c, 0x1e, 0x3d, 0x2c, 0x2e, - 0x24, 0x32, 0x49, 0x40, 0x4c, 0x4b, 0x47, 0x40, - 0x46, 0x45, 0x50, 0x5a, 0x73, 0x62, 0x50, 0x55, - 0x6d, 0x56, 0x45, 0x46, 0x64, 0x88, 0x65, 0x6d, - 0x77, 0x7b, 0x81, 0x82, 0x81, 0x4e, 0x60, 0x8d, - 0x97, 0x8c, 0x7d, 0x96, 0x73, 0x7e, 0x81, 0x7c, -1, /* quantization table part 2 */ - 0x15, 0x17, 0x17, 0x1e, 0x1a, 0x1e, 0x3b, 0x21, - 0x21, 0x3b, 0x7c, 0x53, 0x46, 0x53, 0x7c, 0x0c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, -#elif QUANT_VAL == 1 -/* index 1 - Q50 */ 0, +#define JPEG_QT0_OFFSET 7 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, @@ -72,6 +46,7 @@ static const u8 jpeg_head[] = { 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, 1, +#define JPEG_QT1_OFFSET 72 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, @@ -80,149 +55,6 @@ static const u8 jpeg_head[] = { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, -#elif QUANT_VAL == 2 -/* index 2 Q60 */ -0, - 0x0d, 0x09, 0x0a, 0x0b, 0x0a, 0x08, 0x0d, 0x0b, - 0x0a, 0x0b, 0x0e, 0x0e, 0x0d, 0x0f, 0x13, 0x20, - 0x15, 0x13, 0x12, 0x12, 0x13, 0x27, 0x1c, 0x1e, - 0x17, 0x20, 0x2e, 0x29, 0x31, 0x30, 0x2e, 0x29, - 0x2d, 0x2c, 0x33, 0x3a, 0x4a, 0x3e, 0x33, 0x36, - 0x46, 0x37, 0x2c, 0x2d, 0x40, 0x57, 0x41, 0x46, - 0x4c, 0x4e, 0x52, 0x53, 0x52, 0x32, 0x3e, 0x5a, - 0x61, 0x5a, 0x50, 0x60, 0x4a, 0x51, 0x52, 0x4f, -1, - 0x0e, 0x0e, 0x0e, 0x13, 0x11, 0x13, 0x26, 0x15, - 0x15, 0x26, 0x4f, 0x35, 0x2d, 0x35, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, -#elif QUANT_VAL == 3 -/* index 3 - Q70 */ -0, - 0x0a, 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, - 0x08, 0x08, 0x0b, 0x0a, 0x0a, 0x0b, 0x0e, 0x18, - 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15, 0x16, - 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, - 0x22, 0x21, 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, - 0x34, 0x29, 0x21, 0x22, 0x30, 0x41, 0x31, 0x34, - 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44, - 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, -1, - 0x0a, 0x0b, 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, - 0x10, 0x1c, 0x3b, 0x28, 0x22, 0x28, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, -#elif QUANT_VAL == 4 -/* index 4 - Q80 */ -0, - 0x06, 0x04, 0x05, 0x06, 0x05, 0x04, 0x06, 0x06, - 0x05, 0x06, 0x07, 0x07, 0x06, 0x08, 0x0a, 0x10, - 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, 0x0f, - 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, - 0x16, 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, - 0x23, 0x1c, 0x16, 0x16, 0x20, 0x2c, 0x20, 0x23, - 0x26, 0x27, 0x29, 0x2a, 0x29, 0x19, 0x1f, 0x2d, - 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, 0x28, -1, - 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, - 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, -#elif QUANT_VAL == 5 -/* index 5 - Q85 */ -0, - 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, - 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, - 0x08, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, - 0x09, 0x0c, 0x11, 0x0f, 0x12, 0x12, 0x11, 0x0f, - 0x11, 0x11, 0x13, 0x16, 0x1c, 0x17, 0x13, 0x14, - 0x1a, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1a, - 0x1d, 0x1d, 0x1f, 0x1f, 0x1f, 0x13, 0x17, 0x22, - 0x24, 0x22, 0x1e, 0x24, 0x1c, 0x1e, 0x1f, 0x1e, -1, - 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0e, 0x08, - 0x08, 0x0e, 0x1e, 0x14, 0x11, 0x14, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, -#elif QUANT_VAL == 6 -/* index 6 - 86 */ -0, - 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0B, - 0x07, 0x07, 0x06, 0x06, 0x07, 0x0e, 0x0a, 0x0a, - 0x08, 0x0B, 0x10, 0x0e, 0x11, 0x11, 0x10, 0x0e, - 0x10, 0x0f, 0x12, 0x14, 0x1a, 0x16, 0x12, 0x13, - 0x18, 0x13, 0x0f, 0x10, 0x16, 0x1f, 0x17, 0x18, - 0x1b, 0x1b, 0x1d, 0x1d, 0x1d, 0x11, 0x16, 0x20, - 0x22, 0x1f, 0x1c, 0x22, 0x1a, 0x1c, 0x1d, 0x1c, -1, - 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0D, 0x07, - 0x07, 0x0D, 0x1c, 0x12, 0x10, 0x12, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, -#elif QUANT_VAL == 7 -/* index 7 - 88 */ -0, - 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x06, 0x0a, - 0x06, 0x06, 0x05, 0x05, 0x06, 0x0C, 0x08, 0x09, - 0x07, 0x0a, 0x0e, 0x0c, 0x0f, 0x0e, 0x0e, 0x0c, - 0x0d, 0x0d, 0x0f, 0x11, 0x16, 0x13, 0x0f, 0x10, - 0x15, 0x11, 0x0d, 0x0d, 0x13, 0x1a, 0x13, 0x15, - 0x17, 0x18, 0x19, 0x19, 0x19, 0x0f, 0x12, 0x1b, - 0x1d, 0x1b, 0x18, 0x1d, 0x16, 0x18, 0x19, 0x18, -1, - 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0B, 0x06, - 0x06, 0x0B, 0x18, 0x10, 0x0d, 0x10, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -#elif QUANT_VAL == 8 -/* index 8 - ?? */ -0, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x04, 0x05, - 0x04, 0x05, 0x07, 0x06, 0x08, 0x08, 0x07, 0x06, - 0x07, 0x07, 0x08, 0x09, 0x0c, 0x0a, 0x08, 0x09, - 0x0B, 0x09, 0x07, 0x07, 0x0a, 0x0e, 0x0a, 0x0b, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x08, 0x0a, 0x0e, - 0x0f, 0x0e, 0x0d, 0x0f, 0x0c, 0x0d, 0x0d, 0x0c, -1, - 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, - 0x03, 0x06, 0x0c, 0x08, 0x07, 0x08, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, -#else -#error "Invalid quantization table" -#endif /* huffman table */ 0xff, 0xc4, 0x01, 0xa2, @@ -280,55 +112,57 @@ static const u8 jpeg_head[] = { 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, #ifdef CONEX_CAM /* the Conexant frames start with SOF0 */ +#define JPEG_HDR_SZ 556 #else 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ 0x08, /* data precision */ -#endif -}; - -#ifndef CONEX_CAM -/* variable part: - * 0x01, 0xe0, height - * 0x02, 0x80, width - * 0x03, component number - * 0x01, - * 0x21, samples Y - */ - -/* end of header */ -static u8 eoh[] = { +#define JPEG_HEIGHT_OFFSET 561 + 0x01, 0xe0, /* height */ + 0x02, 0x80, /* width */ + 0x03, /* component number */ + 0x01, + 0x21, /* samples Y */ 0x00, /* quant Y */ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 -}; +#define JPEG_HDR_SZ 589 #endif +}; -/* -- output the JPEG header -- */ -static void jpeg_put_header(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - int samplesY) +/* define the JPEG header */ +static void jpeg_define(u8 *jpeg_hdr, + int height, + int width, + int samplesY) { + memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); #ifndef CONEX_CAM - u8 tmpbuf[8]; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height & 0xff; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width & 0xff; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY; #endif +} - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - jpeg_head, sizeof jpeg_head); -#ifndef CONEX_CAM - tmpbuf[0] = gspca_dev->height >> 8; - tmpbuf[1] = gspca_dev->height & 0xff; - tmpbuf[2] = gspca_dev->width >> 8; - tmpbuf[3] = gspca_dev->width & 0xff; - tmpbuf[4] = 0x03; /* component number */ - tmpbuf[5] = 0x01; /* first component */ - tmpbuf[6] = samplesY; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - tmpbuf, 7); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - eoh, sizeof eoh); -#endif +/* set the JPEG quality */ +static void jpeg_set_qual(u8 *jpeg_hdr, + int quality) +{ + int i, sc; + + if (quality < 50) + sc = 5000 / quality; + else + sc = 200 - quality * 2; + for (i = 0; i < 64; i++) { + jpeg_hdr[JPEG_QT0_OFFSET + i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + jpeg_hdr[JPEG_QT1_OFFSET + i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + } } #endif diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index ce065d363e8a..6eb813e7b714 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -22,7 +22,6 @@ #define MODULE_NAME "mars" #include "gspca.h" -#define QUANT_VAL 1 /* quantization table */ #include "jpeg.h" MODULE_AUTHOR("Michel Xhaard "); @@ -37,6 +36,9 @@ struct sd { u8 colors; u8 gamma; u8 sharpness; + u8 quality; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -176,6 +178,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->colors = COLOR_DEF; sd->gamma = GAMMA_DEF; sd->sharpness = SHARPNESS_DEF; + sd->quality = 50; gspca_dev->nbalt = 9; /* use the altsetting 08 */ return 0; } @@ -193,6 +196,12 @@ static int sd_start(struct gspca_dev *gspca_dev) u8 *data; int i; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + data = gspca_dev->usb_buf; data[0] = 0x01; /* address */ @@ -303,11 +312,19 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_ERR, "Camera Stop failed"); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; int p; if (len < 6) { @@ -330,7 +347,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame, data, p); /* put the JPEG header */ - jpeg_put_header(gspca_dev, frame, 0x21); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); data += p + 16; len -= p + 16; break; @@ -436,6 +454,7 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, }; diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index edc26d8ad2ad..363c0fa39d9e 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -22,7 +22,6 @@ #define MODULE_NAME "sonixj" #include "gspca.h" -#define QUANT_VAL 4 /* quantization table */ #include "jpeg.h" #define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0) @@ -47,6 +46,10 @@ struct sd { u8 gamma; u8 vflip; /* ov7630/ov7648 only */ u8 infrared; /* mt9v111 only */ + u8 quality; /* image quality */ + u8 jpegqual; /* webcam quality */ + + u8 reg18; s8 ag_cnt; #define AG_CNT_START 13 @@ -68,6 +71,8 @@ struct sd { #define SENSOR_OV7660 7 #define SENSOR_SP80708 8 u8 i2c_base; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -859,25 +864,6 @@ static const u8 sp80708_sensor_init[][8] = { {} }; -static const u8 qtable4[] = { - 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, - 0x06, 0x06, 0x08, 0x06, 0x06, 0x08, 0x0a, 0x11, - 0x0a, 0x0a, 0x08, 0x08, 0x0a, 0x15, 0x0f, 0x0f, - 0x0c, 0x11, 0x19, 0x15, 0x19, 0x19, 0x17, 0x15, - 0x17, 0x17, 0x1b, 0x1d, 0x25, 0x21, 0x1b, 0x1d, - 0x23, 0x1d, 0x17, 0x17, 0x21, 0x2e, 0x21, 0x23, - 0x27, 0x29, 0x2c, 0x2c, 0x2c, 0x19, 0x1f, 0x30, - 0x32, 0x2e, 0x29, 0x32, 0x25, 0x29, 0x2c, 0x29, - 0x06, 0x08, 0x08, 0x0a, 0x08, 0x0a, 0x13, 0x0a, - 0x0a, 0x13, 0x29, 0x1b, 0x17, 0x1b, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29 -}; - /* read bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, u16 value, int len) @@ -1309,6 +1295,8 @@ static int sd_config(struct gspca_dev *gspca_dev, else sd->vflip = 1; sd->infrared = INFRARED_DEF; + sd->quality = 80; + sd->jpegqual = 80; gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; return 0; @@ -1610,12 +1598,49 @@ static void setinfrared(struct sd *sd) sd->infrared ? 0x66 : 0x64); } +static void setjpegqual(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, sc; + + if (sd->jpegqual < 50) + sc = 5000 / sd->jpegqual; + else + sc = 200 - sd->jpegqual * 2; +#if USB_BUF_SZ < 64 +#error "No room enough in usb_buf for quantization table" +#endif + for (i = 0; i < 64; i++) + gspca_dev->usb_buf[i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x0100, 0, + gspca_dev->usb_buf, 64, + 500); + for (i = 0; i < 64; i++) + gspca_dev->usb_buf[i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x0140, 0, + gspca_dev->usb_buf, 64, + 500); + + sd->reg18 ^= 0x40; + reg_w1(gspca_dev, 0x18, sd->reg18); +} + /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - u8 reg1, reg17, reg18; + u8 reg1, reg17; const u8 *sn9c1xx; int mode; static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; @@ -1624,6 +1649,12 @@ static int sd_start(struct gspca_dev *gspca_dev) static const u8 CE_ov76xx[] = { 0x32, 0xdd, 0x32, 0xdd }; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + sn9c1xx = sn_tb[(int) sd->sensor]; configure_gpio(gspca_dev, sn9c1xx); @@ -1782,13 +1813,9 @@ static int sd_start(struct gspca_dev *gspca_dev) } /* here change size mode 0 -> VGA; 1 -> CIF */ - reg18 = sn9c1xx[0x18] | (mode << 4); - reg_w1(gspca_dev, 0x18, reg18 | 0x40); - - reg_w(gspca_dev, 0x0100, qtable4, 0x40); - reg_w(gspca_dev, 0x0140, qtable4 + 0x40, 0x40); - - reg_w1(gspca_dev, 0x18, reg18); + sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40; + reg_w1(gspca_dev, 0x18, sd->reg18); + setjpegqual(gspca_dev); reg_w1(gspca_dev, 0x17, reg17); reg_w1(gspca_dev, 0x01, reg1); @@ -1845,6 +1872,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xf1, 0x00); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1928,7 +1962,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (gspca_dev->last_packet_type == LAST_PACKET) { /* put the JPEG 422 header */ - jpeg_put_header(gspca_dev, frame, 0x21); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); } gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } @@ -2104,6 +2139,7 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index f44613095d2e..2176ac6850e3 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -22,7 +22,6 @@ #define MODULE_NAME "spca500" #include "gspca.h" -#define QUANT_VAL 5 /* quantization table */ #include "jpeg.h" MODULE_AUTHOR("Michel Xhaard "); @@ -39,6 +38,7 @@ struct sd { unsigned char brightness; unsigned char contrast; unsigned char colors; + u8 quality; char subtype; #define AgfaCl20 0 @@ -56,6 +56,8 @@ struct sd { #define Optimedia 12 #define PalmPixDC85 13 #define ToptroIndus 14 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -640,6 +642,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->quality = 85; return 0; } @@ -665,6 +668,12 @@ static int sd_start(struct gspca_dev *gspca_dev) __u8 Data; __u8 xmult, ymult; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + if (sd->subtype == LogitechClickSmart310) { xmult = 0x16; ymult = 0x12; @@ -880,6 +889,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0]); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -900,7 +916,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); data += SPCA500_OFFSET_DATA; len -= SPCA500_OFFSET_DATA; @@ -1013,6 +1030,7 @@ static struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, }; diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index d1d54edd80bd..dd007cb52006 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -21,8 +21,6 @@ #define MODULE_NAME "stk014" #include "gspca.h" -#define QUANT_VAL 7 /* quantization table */ - /* <= 4 KO - 7: good (enough!) */ #include "jpeg.h" MODULE_AUTHOR("Jean-Francois Moine "); @@ -37,6 +35,9 @@ struct sd { unsigned char contrast; unsigned char colors; unsigned char lightfreq; + u8 quality; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -300,6 +301,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; sd->lightfreq = FREQ_DEF; + sd->quality = 80; return 0; } @@ -323,8 +325,15 @@ static int sd_init(struct gspca_dev *gspca_dev) /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int ret, value; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + /* work on alternate 1 */ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); @@ -396,11 +405,19 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "camera stopped"); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; static unsigned char ffd9[] = {0xff, 0xd9}; /* a frame starts with: @@ -417,7 +434,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG 411 header */ - jpeg_put_header(gspca_dev, frame, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); /* beginning of the frame */ #define STKHDRSZ 12 @@ -526,6 +544,7 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 9d08a66fe23d..eadfaa9f97d2 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -22,7 +22,6 @@ #define MODULE_NAME "sunplus" #include "gspca.h" -#define QUANT_VAL 5 /* quantization table */ #include "jpeg.h" MODULE_AUTHOR("Michel Xhaard "); @@ -40,6 +39,7 @@ struct sd { unsigned char contrast; unsigned char colors; unsigned char autogain; + u8 quality; char bridge; #define BRIDGE_SPCA504 0 @@ -52,6 +52,8 @@ struct sd { #define LogitechClickSmart420 2 #define LogitechClickSmart820 3 #define MegapixV4 4 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -852,6 +854,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->quality = 85; return 0; } @@ -968,6 +971,12 @@ static int sd_start(struct gspca_dev *gspca_dev) __u8 i; __u8 info[6]; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + if (sd->bridge == BRIDGE_SPCA504B) spca504B_setQtable(gspca_dev); spca504B_SetSizeType(gspca_dev); @@ -1077,6 +1086,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1153,7 +1169,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); } /* add 0x00 after 0xff */ @@ -1311,6 +1328,7 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, }; diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 4f459a746037..a4c673ff8f02 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -23,6 +23,7 @@ #define MODULE_NAME "zc3xx" #include "gspca.h" +#include "jpeg.h" MODULE_AUTHOR("Michel Xhaard , " "Serge A. Suchkov "); @@ -32,7 +33,6 @@ MODULE_LICENSE("GPL"); static int force_sensor = -1; #define QUANT_VAL 1 /* quantization table */ -#include "jpeg.h" #include "zc3xx-reg.h" /* specific webcam descriptor */ @@ -45,6 +45,7 @@ struct sd { __u8 autogain; __u8 lightfreq; __u8 sharpness; + u8 quality; /* image quality */ signed char sensor; /* Type of image sensor chip */ /* !! values used in different tables */ @@ -69,6 +70,8 @@ struct sd { #define SENSOR_TAS5130C_VF0250 17 #define SENSOR_MAX 18 unsigned short chip_revision; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -7177,6 +7180,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->gamma = gamma[(int) sd->sensor]; sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; + sd->quality = 50; switch (sd->sensor) { case SENSOR_GC0305: @@ -7232,6 +7236,12 @@ static int sd_start(struct gspca_dev *gspca_dev) /* 17 */ }; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; zc3_init = init_tb[(int) sd->sensor][mode]; switch (sd->sensor) { @@ -7365,6 +7375,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + kfree(sd->jpeg_hdr); if (!gspca_dev->present) return; send_unknown(gspca_dev->dev, sd->sensor); @@ -7375,12 +7386,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, int len) { + struct sd *sd = (struct sd *) gspca_dev; if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, 0x21); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); + /* remove the webcam's header: * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp * - 'ss ss' is the frame sequence number (BE) -- cgit v1.2.3-59-g8ed1b From ae6cfaace120f4330715b56265ce0e4a710e1276 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Mar 2009 08:28:45 -0300 Subject: V4L/DVB (11044): v4l2-device: add v4l2_device_disconnect Call v4l2_device_disconnect when the parent of a hotpluggable device disconnects. This ensures that you do not have a pointer to a device that is no longer present. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.bttv | 1 - Documentation/video4linux/v4l2-framework.txt | 11 +++++++++++ drivers/media/video/v4l2-device.c | 15 +++++++++++---- include/media/v4l2-device.h | 6 +++++- 4 files changed, 27 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index f11c583295e9..e17750473e08 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -157,4 +157,3 @@ 156 -> IVCE-8784 [0000:f050,0001:f050,0002:f050,0003:f050] 157 -> Geovision GV-800(S) (master) [800a:763d] 158 -> Geovision GV-800(S) (slave) [800b:763d,800c:763d,800d:763d] -159 -> ProVideo PV183 [1830:1540,1831:1540,1832:1540,1833:1540,1834:1540,1835:1540,1836:1540,1837:1540] diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 4207590b2ac8..a31177390e55 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -105,6 +105,17 @@ You unregister with: Unregistering will also automatically unregister all subdevs from the device. +If you have a hotpluggable device (e.g. a USB device), then when a disconnect +happens the parent device becomes invalid. Since v4l2_device has a pointer to +that parent device it has to be cleared as well to mark that the parent is +gone. To do this call: + + v4l2_device_disconnect(struct v4l2_device *v4l2_dev); + +This does *not* unregister the subdevs, so you still need to call the +v4l2_device_unregister() function for that. If your driver is not hotpluggable, +then there is no need to call v4l2_device_disconnect(). + Sometimes you need to iterate over all devices registered by a specific driver. This is usually the case if multiple device drivers use the same hardware. E.g. the ivtvfb driver is a framebuffer driver that uses the ivtv diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index b3dcb8454379..94aa485ade52 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -49,19 +49,26 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) } EXPORT_SYMBOL_GPL(v4l2_device_register); +void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) +{ + if (v4l2_dev->dev) { + dev_set_drvdata(v4l2_dev->dev, NULL); + v4l2_dev->dev = NULL; + } +} +EXPORT_SYMBOL_GPL(v4l2_device_disconnect); + void v4l2_device_unregister(struct v4l2_device *v4l2_dev) { struct v4l2_subdev *sd, *next; if (v4l2_dev == NULL) return; - if (v4l2_dev->dev) - dev_set_drvdata(v4l2_dev->dev, NULL); + v4l2_device_disconnect(v4l2_dev); + /* Unregister subdevs */ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) v4l2_device_unregister_subdev(sd); - - v4l2_dev->dev = NULL; } EXPORT_SYMBOL_GPL(v4l2_device_unregister); diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 3d8e96f6ceb3..0dd3e8e8653e 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -53,7 +53,11 @@ struct v4l2_device { dev may be NULL in rare cases (ISA devices). In that case you must fill in the v4l2_dev->name field before calling this function. */ int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); -/* Set v4l2_dev->dev->driver_data to NULL and unregister all sub-devices */ +/* Set v4l2_dev->dev to NULL. Call when the USB parent disconnects. + Since the parent disappears this ensures that v4l2_dev doesn't have an + invalid parent pointer. */ +void v4l2_device_disconnect(struct v4l2_device *v4l2_dev); +/* Unregister all sub-devices and any other resources related to v4l2_dev. */ void v4l2_device_unregister(struct v4l2_device *v4l2_dev); /* Register a subdev with a v4l2 device. While registered the subdev module -- cgit v1.2.3-59-g8ed1b From 15965f063d6d42bd451415923c4cccbdb4a61bdd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 14 Mar 2009 15:06:08 -0300 Subject: V4L/DVB (11051): v4l-dvb: replace remaining references to the old mailinglist. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/bttv/README | 4 ++-- drivers/media/radio/radio-si470x.c | 2 +- drivers/media/video/bt8xx/bttv-cards.c | 2 +- drivers/media/video/usbvision/usbvision.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/bttv/README b/Documentation/video4linux/bttv/README index 7ca2154c2bf5..3a367cdb664e 100644 --- a/Documentation/video4linux/bttv/README +++ b/Documentation/video4linux/bttv/README @@ -63,8 +63,8 @@ If you have some knowledge and spare time, please try to fix this yourself (patches very welcome of course...) You know: The linux slogan is "Do it yourself". -There is a mailing list: video4linux-list@redhat.com. -https://listman.redhat.com/mailman/listinfo/video4linux-list +There is a mailing list: linux-media@vger.kernel.org +http://vger.kernel.org/vger-lists.html#linux-media If you have trouble with some specific TV card, try to ask there instead of mailing me directly. The chance that someone with the diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 9827445086aa..713e242ba8b2 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -1713,7 +1713,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, ": If you have some trouble using this driver,\n"); printk(KERN_WARNING DRIVER_NAME ": please report to V4L ML at " - "video4linux-list@redhat.com\n"); + "linux-media@vger.kernel.org\n"); } /* set initial frequency */ diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 187ed4ef3993..7725d9487abf 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -2924,7 +2924,7 @@ void __devinit bttv_idcard(struct bttv *btv) btv->c.nr, btv->cardid & 0xffff, (btv->cardid >> 16) & 0xffff); printk(KERN_DEBUG "please mail id, board name and " - "the correct card= insmod option to video4linux-list@redhat.com\n"); + "the correct card= insmod option to linux-media@vger.kernel.org\n"); } } diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index 06fe43655957..f8d7458daf3e 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -6,7 +6,7 @@ * Dwaine Garden * * - * Report problems to v4l MailingList : http://www.redhat.com/mailman/listinfo/video4linux-list + * Report problems to v4l MailingList: linux-media@vger.kernel.org * * This module is part of usbvision driver project. * Updates to driver completed by Dwaine P. Garden -- cgit v1.2.3-59-g8ed1b From 06e9509ff3484578c111bbc6f9e0e97c086a1131 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 19 Mar 2009 12:10:00 -0300 Subject: V4L/DVB (11108): get_dvb_firmware: Add option to download firmware for cx231xx Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index f2e908d7f90d..72455b6d9008 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -25,7 +25,7 @@ use IO::Handle; "tda10046lifeview", "av7110", "dec2000t", "dec2540t", "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", - "opera1"); + "opera1", "cx231xx"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -345,6 +345,19 @@ sub or51211 { $fwfile; } +sub cx231xx { + my $fwfile = "v4l-cx231xx-avcore-01.fw"; + my $url = "http://linuxtv.org/downloads/firmware/$fwfile"; + my $hash = "7d3bb956dc9df0eafded2b56ba57cc42"; + + checkstandard(); + + wgetfile($fwfile, $url); + verify($fwfile, $hash); + + $fwfile; +} + sub or51132_qam { my $fwfile = "dvb-fe-or51132-qam.fw"; my $url = "http://linuxtv.org/downloads/firmware/$fwfile"; -- cgit v1.2.3-59-g8ed1b From dceaddb978a7fcd2efbdf6775a509529757327c3 Mon Sep 17 00:00:00 2001 From: Alan McIvor Date: Thu, 12 Mar 2009 21:43:34 -0300 Subject: V4L/DVB (11124): Add support for ProVideo PV-183 to bttv Add support for ProVideo PV-183 to bttv This patch adds support for the ProVideo PV-183 card to the bttv device driver. The PV-183 is a PCI card with 8 BT878 devices plus a Hint Corp HiNT HB4 PCI-PCI Bridge. Each BT878 has two composite input channels available. There are no tuners on this card. Signed-off-by: Alan McIvor Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.bttv | 1 + drivers/media/video/bt8xx/bttv-cards.c | 24 ++++++++++++++++++++++++ drivers/media/video/bt8xx/bttv.h | 1 + 3 files changed, 26 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index e17750473e08..f11c583295e9 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -157,3 +157,4 @@ 156 -> IVCE-8784 [0000:f050,0001:f050,0002:f050,0003:f050] 157 -> Geovision GV-800(S) (master) [800a:763d] 158 -> Geovision GV-800(S) (slave) [800b:763d,800c:763d,800d:763d] +159 -> ProVideo PV183 [1830:1540,1831:1540,1832:1540,1833:1540,1834:1540,1835:1540,1836:1540,1837:1540] diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 7725d9487abf..1536ab5a4a8d 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -320,6 +320,16 @@ static struct CARD { { 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, { 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, { 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, + + { 0x15401830, BTTV_BOARD_PV183, "Provideo PV183-1" }, + { 0x15401831, BTTV_BOARD_PV183, "Provideo PV183-2" }, + { 0x15401832, BTTV_BOARD_PV183, "Provideo PV183-3" }, + { 0x15401833, BTTV_BOARD_PV183, "Provideo PV183-4" }, + { 0x15401834, BTTV_BOARD_PV183, "Provideo PV183-5" }, + { 0x15401835, BTTV_BOARD_PV183, "Provideo PV183-6" }, + { 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" }, + { 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" }, + { 0, -1, NULL } }; @@ -2881,6 +2891,20 @@ struct tvcard bttv_tvcards[] = { .no_tda9875 = 1, .muxsel_hook = gv800s_muxsel, }, + [BTTV_BOARD_PV183] = { + .name = "ProVideo PV183", /* 0x9f */ + .video_inputs = 2, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .gpiomask = 0, + .muxsel = MUXSEL(2, 3), + .gpiomux = { 0 }, + .needs_tvaudio = 0, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 85b0e3e9d382..fac5f86356d2 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -184,6 +184,7 @@ #define BTTV_BOARD_IVCE8784 0x9c #define BTTV_BOARD_GEOVISION_GV800S 0x9d #define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e +#define BTTV_BOARD_PV183 0x9f /* more card-specific defines */ -- cgit v1.2.3-59-g8ed1b From b888c5dadb4ae409964bd7b9bedfac507ab10972 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 23 Mar 2009 10:57:15 -0300 Subject: V4L/DVB (11136): get_dvb_firmware: Add download code for cx18 firmwares Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 72455b6d9008..2d32590a1f68 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -25,7 +25,7 @@ use IO::Handle; "tda10046lifeview", "av7110", "dec2000t", "dec2540t", "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", - "opera1", "cx231xx"); + "opera1", "cx231xx", "cx18", "cx23885" ); # Check args syntax() if (scalar(@ARGV) != 1); @@ -37,8 +37,8 @@ for ($i=0; $i < scalar(@components); $i++) { $outfile = eval($cid); die $@ if $@; print STDERR < '588f081b562f5c653a3db1ad8f65939a', + 'v4l-cx23418-cpu.fw' => 'b6c7ed64bc44b1a6e0840adaeac39d79', + 'v4l-cx23418-dig.fw' => '95bc688d3e7599fd5800161e9971cc55', + ); + + checkstandard(); + + my $allfiles; + foreach my $fwfile (keys %files) { + wgetfile($fwfile, "$url/$fwfile"); + verify($fwfile, $files{$fwfile}); + $allfiles .= " $fwfile"; + } + + $allfiles =~ s/^\s//; + + $allfiles; +} + sub or51132_qam { my $fwfile = "dvb-fe-or51132-qam.fw"; my $url = "http://linuxtv.org/downloads/firmware/$fwfile"; -- cgit v1.2.3-59-g8ed1b From 5297e6f7e9818a43228f6b4131c4630f08075093 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 23 Mar 2009 11:46:19 -0300 Subject: V4L/DVB (11137): get_dvb_firmware: add cx23885 firmwares Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'Documentation') diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 2d32590a1f68..7e485eabda7d 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -381,6 +381,29 @@ sub cx18 { $allfiles; } +sub cx23885 { + my $url = "http://linuxtv.org/downloads/firmware/"; + + my %files = ( + 'v4l-cx23885-avcore-01.fw' => 'a9f8f5d901a7fb42f552e1ee6384f3bb', + 'v4l-cx23885-enc.fw' => 'a9f8f5d901a7fb42f552e1ee6384f3bb', + 'v4l-cx25840.fw' => 'dadb79e9904fc8af96e8111d9cb59320', + ); + + checkstandard(); + + my $allfiles; + foreach my $fwfile (keys %files) { + wgetfile($fwfile, "$url/$fwfile"); + verify($fwfile, $files{$fwfile}); + $allfiles .= " $fwfile"; + } + + $allfiles =~ s/^\s//; + + $allfiles; +} + sub or51132_qam { my $fwfile = "dvb-fe-or51132-qam.fw"; my $url = "http://linuxtv.org/downloads/firmware/$fwfile"; -- cgit v1.2.3-59-g8ed1b From f65a95bbf4f23dd83596e7c2dd78f4e1cc268c4f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 23 Mar 2009 12:35:03 -0300 Subject: V4L/DVB (11138): get_dvb_firmware: add support for downloading the cx2584x firmware for pvrusb2 Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 7e485eabda7d..2f21ecd4c205 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -25,7 +25,7 @@ use IO::Handle; "tda10046lifeview", "av7110", "dec2000t", "dec2540t", "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", - "opera1", "cx231xx", "cx18", "cx23885" ); + "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2" ); # Check args syntax() if (scalar(@ARGV) != 1); @@ -387,6 +387,26 @@ sub cx23885 { my %files = ( 'v4l-cx23885-avcore-01.fw' => 'a9f8f5d901a7fb42f552e1ee6384f3bb', 'v4l-cx23885-enc.fw' => 'a9f8f5d901a7fb42f552e1ee6384f3bb', + ); + + checkstandard(); + + my $allfiles; + foreach my $fwfile (keys %files) { + wgetfile($fwfile, "$url/$fwfile"); + verify($fwfile, $files{$fwfile}); + $allfiles .= " $fwfile"; + } + + $allfiles =~ s/^\s//; + + $allfiles; +} + +sub pvrusb2 { + my $url = "http://linuxtv.org/downloads/firmware/"; + + my %files = ( 'v4l-cx25840.fw' => 'dadb79e9904fc8af96e8111d9cb59320', ); -- cgit v1.2.3-59-g8ed1b From 7ddfda9aa6a735c2b883b8cf677099bce27bf708 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 26 Mar 2009 05:13:40 -0300 Subject: V4L/DVB (11223): gspca - doc: Add the 15b8:6001 webcam to the Documentation. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 12c8ff7058b5..98529e03a46e 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -282,6 +282,7 @@ spca561 10fd:7e50 FlyCam Usb 100 zc3xx 10fd:8050 Typhoon Webshot II USB 300k ov534 1415:2000 Sony HD Eye for PS3 (SLEH 00201) pac207 145f:013a Trust WB-1300N +vc032x 15b8:6001 HP 2.0 Megapixel vc032x 15b8:6002 HP 2.0 Megapixel rz406aa spca501 1776:501c Arowana 300K CMOS Camera t613 17a1:0128 TASCORP JPEG Webcam, NGS Cyclops -- cgit v1.2.3-59-g8ed1b From aeecea26234011c3fbf65ffa7d9c9525b486acab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 26 Mar 2009 12:07:36 -0300 Subject: V4L/DVB (11225): v4lgrab: fix compilation warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documentation/video4linux/v4lgrab.c: In function ‘main’: Documentation/video4linux/v4lgrab.c:193: warning: ‘src_depth’ is used uninitialized in this function Documentation/video4linux/v4lgrab.c:108: warning: ‘b’ may be used uninitialized in this function Documentation/video4linux/v4lgrab.c:108: warning: ‘g’ may be used uninitialized in this function Documentation/video4linux/v4lgrab.c:108: warning: ‘r’ may be used uninitialized in this function Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4lgrab.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4lgrab.c b/Documentation/video4linux/v4lgrab.c index d6e70bef8ad0..05769cff1009 100644 --- a/Documentation/video4linux/v4lgrab.c +++ b/Documentation/video4linux/v4lgrab.c @@ -105,8 +105,8 @@ int main(int argc, char ** argv) struct video_picture vpic; unsigned char *buffer, *src; - int bpp = 24, r, g, b; - unsigned int i, src_depth; + int bpp = 24, r = 0, g = 0, b = 0; + unsigned int i, src_depth = 16; if (fd < 0) { perror(VIDEO_DEV); -- cgit v1.2.3-59-g8ed1b From 25f3311acc3405dd0dace3571a41f450e6cc6a65 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:41 +0200 Subject: hwmon: (ds1621) Clean up documentation * The alarms sysfs file is deprecated, and individual alarm files are self-explanatory. * The driver doesn't implement high-reslution temperature readings so don't document that. Signed-off-by: Jean Delvare Cc: Aurelien Jarno --- Documentation/hwmon/ds1621 | 51 +++------------------------------------------- 1 file changed, 3 insertions(+), 48 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/ds1621 b/Documentation/hwmon/ds1621 index 1fee6f1e6bc5..5e97f333c4df 100644 --- a/Documentation/hwmon/ds1621 +++ b/Documentation/hwmon/ds1621 @@ -49,12 +49,9 @@ of up to +/- 0.5 degrees even when compared against precise temperature readings. Be sure to have a high vs. low temperature limit gap of al least 1.0 degree Celsius to avoid Tout "bouncing", though! -As for alarms, you can read the alarm status of the DS1621 via the 'alarms' -/sys file interface. The result consists mainly of bit 6 and 5 of the -configuration register of the chip; bit 6 (0x40 or 64) is the high alarm -bit and bit 5 (0x20 or 32) the low one. These bits are set when the high or -low limits are met or exceeded and are reset by the module as soon as the -respective temperature ranges are left. +The alarm bits are set when the high or low limits are met or exceeded and +are reset by the module as soon as the respective temperature ranges are +left. The alarm registers are in no way suitable to find out about the actual status of Tout. They will only tell you about its history, whether or not @@ -64,45 +61,3 @@ with neither of the alarms set. Temperature conversion of the DS1621 takes up to 1000ms; internal access to non-volatile registers may last for 10ms or below. - -High Accuracy Temperature Reading ---------------------------------- - -As said before, the temperature issued via the 9-bit i2c-bus data is -somewhat arbitrary. Internally, the temperature conversion is of a -different kind that is explained (not so...) well in the DS1621 data sheet. -To cut the long story short: Inside the DS1621 there are two oscillators, -both of them biassed by a temperature coefficient. - -Higher resolution of the temperature reading can be achieved using the -internal projection, which means taking account of REG_COUNT and REG_SLOPE -(the driver manages them): - -Taken from Dallas Semiconductors App Note 068: 'Increasing Temperature -Resolution on the DS1620' and App Note 105: 'High Resolution Temperature -Measurement with Dallas Direct-to-Digital Temperature Sensors' - -- Read the 9-bit temperature and strip the LSB (Truncate the .5 degs) -- The resulting value is TEMP_READ. -- Then, read REG_COUNT. -- And then, REG_SLOPE. - - TEMP = TEMP_READ - 0.25 + ((REG_SLOPE - REG_COUNT) / REG_SLOPE) - -Note that this is what the DONE bit in the DS1621 configuration register is -good for: Internally, one temperature conversion takes up to 1000ms. Before -that conversion is complete you will not be able to read valid things out -of REG_COUNT and REG_SLOPE. The DONE bit, as you may have guessed by now, -tells you whether the conversion is complete ("done", in plain English) and -thus, whether the values you read are good or not. - -The DS1621 has two modes of operation: "Continuous" conversion, which can -be understood as the default stand-alone mode where the chip gets the -temperature and controls external devices via its Tout pin or tells other -i2c's about it if they care. The other mode is called "1SHOT", that means -that it only figures out about the temperature when it is explicitly told -to do so; this can be seen as power saving mode. - -Now if you want to read REG_COUNT and REG_SLOPE, you have to either stop -the continuous conversions until the contents of these registers are valid, -or, in 1SHOT mode, you have to have one conversion made. -- cgit v1.2.3-59-g8ed1b From 237c8d2f54ff12bd4fea1a9d18a94ae5810271d3 Mon Sep 17 00:00:00 2001 From: Gong Jun Date: Mon, 30 Mar 2009 21:46:42 +0200 Subject: hwmon: (w83627ehf) Add support for W83667HG Add initial support for the Nuvoton W83667HG chip to the w83627ehf driver. It has been tested on ASUS P5QL PRO by Gong Jun. At the moment there is still a usability issue which is that only in6 or temp3 can be present on the W83667HG, so the driver shouldn't expose both. This will be addressed later. Signed-off-by: Gong Jun Acked-by: David Hubbard Signed-off-by: Jean Delvare --- Documentation/hwmon/w83627ehf | 29 ++++++++---- drivers/hwmon/Kconfig | 4 +- drivers/hwmon/w83627ehf.c | 106 +++++++++++++++++++++++++++--------------- 3 files changed, 92 insertions(+), 47 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/w83627ehf b/Documentation/hwmon/w83627ehf index d6e1ae30fa6e..b6eb59384bb3 100644 --- a/Documentation/hwmon/w83627ehf +++ b/Documentation/hwmon/w83627ehf @@ -2,30 +2,40 @@ Kernel driver w83627ehf ======================= Supported chips: - * Winbond W83627EHF/EHG/DHG (ISA access ONLY) + * Winbond W83627EHF/EHG (ISA access ONLY) Prefix: 'w83627ehf' Addresses scanned: ISA address retrieved from Super I/O registers Datasheet: - http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf - DHG datasheet confidential. + http://www.nuvoton.com.tw/NR/rdonlyres/A6A258F0-F0C9-4F97-81C0-C4D29E7E943E/0/W83627EHF.pdf + * Winbond W83627DHG + Prefix: 'w83627dhg' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: + http://www.nuvoton.com.tw/NR/rdonlyres/7885623D-A487-4CF9-A47F-30C5F73D6FE6/0/W83627DHG.pdf + * Winbond W83667HG + Prefix: 'w83667hg' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: not available Authors: Jean Delvare Yuan Mu (Winbond) Rudolf Marek David Hubbard + Gong Jun Description ----------- -This driver implements support for the Winbond W83627EHF, W83627EHG, and -W83627DHG super I/O chips. We will refer to them collectively as Winbond chips. +This driver implements support for the Winbond W83627EHF, W83627EHG, +W83627DHG and W83667HG super I/O chips. We will refer to them collectively +as Winbond chips. The chips implement three temperature sensors, five fan rotation speed sensors, ten analog voltage sensors (only nine for the 627DHG), one -VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG), alarms with beep -warnings (control unimplemented), and some automatic fan regulation -strategies (plus manual fan control mode). +VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG and 667HG), alarms +with beep warnings (control unimplemented), and some automatic fan +regulation strategies (plus manual fan control mode). Temperatures are measured in degrees Celsius and measurement resolution is 1 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when @@ -54,7 +64,8 @@ follows: temp1 -> pwm1 temp2 -> pwm2 temp3 -> pwm3 -prog -> pwm4 (the programmable setting is not supported by the driver) +prog -> pwm4 (not on 667HG; the programmable setting is not supported by + the driver) /sys files ---------- diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b4eea0292c1a..0eb4170cc4af 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -827,7 +827,7 @@ config SENSORS_W83627HF will be called w83627hf. config SENSORS_W83627EHF - tristate "Winbond W83627EHF/DHG" + tristate "Winbond W83627EHF/EHG/DHG, W83667HG" select HWMON_VID help If you say yes here you get support for the hardware @@ -838,6 +838,8 @@ config SENSORS_W83627EHF chip suited for specific Intel processors that use PECI such as the Core 2 Duo. + This driver also supports the W83667HG chip. + This driver can also be built as a module. If so, the module will be called w83627ehf. diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 18432e34dc7a..20a9332959bb 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -36,6 +36,7 @@ w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3 0x8860 0xa1 w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 + w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 */ #include @@ -52,12 +53,13 @@ #include #include "lm75.h" -enum kinds { w83627ehf, w83627dhg }; +enum kinds { w83627ehf, w83627dhg, w83667hg }; /* used to set data->name = w83627ehf_device_names[data->sio_kind] */ static const char * w83627ehf_device_names[] = { "w83627ehf", "w83627dhg", + "w83667hg", }; static unsigned short force_id; @@ -71,6 +73,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); */ #define W83627EHF_LD_HWM 0x0b +#define W83667HG_LD_VID 0x0d #define SIO_REG_LDSEL 0x07 /* Logical device select */ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ @@ -83,6 +86,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); #define SIO_W83627EHF_ID 0x8850 #define SIO_W83627EHG_ID 0x8860 #define SIO_W83627DHG_ID 0xa020 +#define SIO_W83667HG_ID 0xa510 #define SIO_ID_MASK 0xFFF0 static inline void @@ -289,6 +293,7 @@ struct w83627ehf_data { u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ u8 pwm_enable[4]; /* 1->manual 2->thermal cruise (also called SmartFan I) */ + u8 pwm_num; /* number of pwm */ u8 pwm[4]; u8 target_temp[4]; u8 tolerance[4]; @@ -1192,7 +1197,7 @@ static void w83627ehf_device_remove_files(struct device *dev) device_remove_file(dev, &sda_fan_div[i].dev_attr); device_remove_file(dev, &sda_fan_min[i].dev_attr); } - for (i = 0; i < 4; i++) { + for (i = 0; i < data->pwm_num; i++) { device_remove_file(dev, &sda_pwm[i].dev_attr); device_remove_file(dev, &sda_pwm_mode[i].dev_attr); device_remove_file(dev, &sda_pwm_enable[i].dev_attr); @@ -1272,8 +1277,10 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->name = w83627ehf_device_names[sio_data->kind]; platform_set_drvdata(pdev, data); - /* 627EHG and 627EHF have 10 voltage inputs; DHG has 9 */ - data->in_num = (sio_data->kind == w83627dhg) ? 9 : 10; + /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */ + data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9; + /* 667HG has 3 pwms */ + data->pwm_num = (sio_data->kind == w83667hg) ? 3 : 4; /* Initialize the chip */ w83627ehf_init_device(data); @@ -1281,44 +1288,64 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->vrm = vid_which_vrm(); superio_enter(sio_data->sioreg); /* Read VID value */ - superio_select(sio_data->sioreg, W83627EHF_LD_HWM); - if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { - /* Set VID input sensibility if needed. In theory the BIOS - should have set it, but in practice it's not always the - case. We only do it for the W83627EHF/EHG because the - W83627DHG is more complex in this respect. */ - if (sio_data->kind == w83627ehf) { - en_vrm10 = superio_inb(sio_data->sioreg, - SIO_REG_EN_VRM10); - if ((en_vrm10 & 0x08) && data->vrm == 90) { - dev_warn(dev, "Setting VID input voltage to " - "TTL\n"); - superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, - en_vrm10 & ~0x08); - } else if (!(en_vrm10 & 0x08) && data->vrm == 100) { - dev_warn(dev, "Setting VID input voltage to " - "VRM10\n"); - superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, - en_vrm10 | 0x08); - } - } - - data->vid = superio_inb(sio_data->sioreg, SIO_REG_VID_DATA); - if (sio_data->kind == w83627ehf) /* 6 VID pins only */ - data->vid &= 0x3f; - + if (sio_data->kind == w83667hg) { + /* W83667HG has different pins for VID input and output, so + we can get the VID input values directly at logical device D + 0xe3. */ + superio_select(sio_data->sioreg, W83667HG_LD_VID); + data->vid = superio_inb(sio_data->sioreg, 0xe3); err = device_create_file(dev, &dev_attr_cpu0_vid); if (err) goto exit_release; } else { - dev_info(dev, "VID pins in output mode, CPU VID not " - "available\n"); + superio_select(sio_data->sioreg, W83627EHF_LD_HWM); + if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { + /* Set VID input sensibility if needed. In theory the + BIOS should have set it, but in practice it's not + always the case. We only do it for the W83627EHF/EHG + because the W83627DHG is more complex in this + respect. */ + if (sio_data->kind == w83627ehf) { + en_vrm10 = superio_inb(sio_data->sioreg, + SIO_REG_EN_VRM10); + if ((en_vrm10 & 0x08) && data->vrm == 90) { + dev_warn(dev, "Setting VID input " + "voltage to TTL\n"); + superio_outb(sio_data->sioreg, + SIO_REG_EN_VRM10, + en_vrm10 & ~0x08); + } else if (!(en_vrm10 & 0x08) + && data->vrm == 100) { + dev_warn(dev, "Setting VID input " + "voltage to VRM10\n"); + superio_outb(sio_data->sioreg, + SIO_REG_EN_VRM10, + en_vrm10 | 0x08); + } + } + + data->vid = superio_inb(sio_data->sioreg, + SIO_REG_VID_DATA); + if (sio_data->kind == w83627ehf) /* 6 VID pins only */ + data->vid &= 0x3f; + + err = device_create_file(dev, &dev_attr_cpu0_vid); + if (err) + goto exit_release; + } else { + dev_info(dev, "VID pins in output mode, CPU VID not " + "available\n"); + } } /* fan4 and fan5 share some pins with the GPIO and serial flash */ - - fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x2); - fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x6); + if (sio_data->kind == w83667hg) { + fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; + fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; + } else { + fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); + fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); + } superio_exit(sio_data->sioreg); /* It looks like fan4 and fan5 pins can be alternatively used @@ -1344,7 +1371,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) goto exit_remove; /* if fan4 is enabled create the sf3 files for it */ - if (data->has_fan & (1 << 3)) + if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4) for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) { if ((err = device_create_file(dev, &sda_sf3_arrays_fan4[i].dev_attr))) @@ -1372,7 +1399,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) || (err = device_create_file(dev, &sda_fan_min[i].dev_attr))) goto exit_remove; - if (i < 4 && /* w83627ehf only has 4 pwm */ + if (i < data->pwm_num && ((err = device_create_file(dev, &sda_pwm[i].dev_attr)) || (err = device_create_file(dev, @@ -1442,6 +1469,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, static const char __initdata sio_name_W83627EHF[] = "W83627EHF"; static const char __initdata sio_name_W83627EHG[] = "W83627EHG"; static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; + static const char __initdata sio_name_W83667HG[] = "W83667HG"; u16 val; const char *sio_name; @@ -1466,6 +1494,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, sio_data->kind = w83627dhg; sio_name = sio_name_W83627DHG; break; + case SIO_W83667HG_ID: + sio_data->kind = w83667hg; + sio_name = sio_name_W83667HG; + break; default: if (val != 0xffff) pr_debug(DRVNAME ": unsupported chip ID: 0x%04x\n", -- cgit v1.2.3-59-g8ed1b From fb4504fe84b09cbf49fda19e6630a1003d79656a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:43 +0200 Subject: Move the pcf8591 driver to hwmon Directory drivers/i2c/chips is going away, so drivers there must find new homes. For the pcf8591 driver, the best choice seems to be the hwmon subsystem. While the Philips PCF8591 device isn't a typical hardware monitoring chip, its DAC interface is compatible with the hwmon one, so it fits somewhat. If a better subsystem is ever created for ADC/DAC chips, the driver could be moved there. Signed-off-by: Jean Delvare Cc: Aurelien Jarno --- Documentation/hwmon/pcf8591 | 90 +++++++++++ Documentation/i2c/chips/pcf8591 | 90 ----------- drivers/hwmon/Kconfig | 14 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/pcf8591.c | 325 ++++++++++++++++++++++++++++++++++++++++ drivers/i2c/chips/Kconfig | 13 -- drivers/i2c/chips/Makefile | 1 - drivers/i2c/chips/pcf8591.c | 325 ---------------------------------------- 8 files changed, 430 insertions(+), 429 deletions(-) create mode 100644 Documentation/hwmon/pcf8591 delete mode 100644 Documentation/i2c/chips/pcf8591 create mode 100644 drivers/hwmon/pcf8591.c delete mode 100644 drivers/i2c/chips/pcf8591.c (limited to 'Documentation') diff --git a/Documentation/hwmon/pcf8591 b/Documentation/hwmon/pcf8591 new file mode 100644 index 000000000000..5628fcf4207f --- /dev/null +++ b/Documentation/hwmon/pcf8591 @@ -0,0 +1,90 @@ +Kernel driver pcf8591 +===================== + +Supported chips: + * Philips PCF8591 + Prefix: 'pcf8591' + Addresses scanned: I2C 0x48 - 0x4f + Datasheet: Publicly available at the Philips Semiconductor website + http://www.semiconductors.philips.com/pip/PCF8591P.html + +Authors: + Aurelien Jarno + valuable contributions by Jan M. Sendler , + Jean Delvare + + +Description +----------- +The PCF8591 is an 8-bit A/D and D/A converter (4 analog inputs and one +analog output) for the I2C bus produced by Philips Semiconductors. It +is designed to provide a byte I2C interface to up to 4 separate devices. + +The PCF8591 has 4 analog inputs programmable as single-ended or +differential inputs : +- mode 0 : four single ended inputs + Pins AIN0 to AIN3 are single ended inputs for channels 0 to 3 + +- mode 1 : three differential inputs + Pins AIN3 is the common negative differential input + Pins AIN0 to AIN2 are positive differential inputs for channels 0 to 2 + +- mode 2 : single ended and differential mixed + Pins AIN0 and AIN1 are single ended inputs for channels 0 and 1 + Pins AIN2 is the positive differential input for channel 3 + Pins AIN3 is the negative differential input for channel 3 + +- mode 3 : two differential inputs + Pins AIN0 is the positive differential input for channel 0 + Pins AIN1 is the negative differential input for channel 0 + Pins AIN2 is the positive differential input for channel 1 + Pins AIN3 is the negative differential input for channel 1 + +See the datasheet for details. + +Module parameters +----------------- + +* input_mode int + + Analog input mode: + 0 = four single ended inputs + 1 = three differential inputs + 2 = single ended and differential mixed + 3 = two differential inputs + + +Accessing PCF8591 via /sys interface +------------------------------------- + +! Be careful ! +The PCF8591 is plainly impossible to detect ! Stupid chip. +So every chip with address in the interval [48..4f] is +detected as PCF8591. If you have other chips in this address +range, the workaround is to load this module after the one +for your others chips. + +On detection (i.e. insmod, modprobe et al.), directories are being +created for each detected PCF8591: + +/sys/bus/devices/<0>-<1>/ +where <0> is the bus the chip was detected on (e. g. i2c-0) +and <1> the chip address ([48..4f]) + +Inside these directories, there are such files: +in0, in1, in2, in3, out0_enable, out0_output, name + +Name contains chip name. + +The in0, in1, in2 and in3 files are RO. Reading gives the value of the +corresponding channel. Depending on the current analog inputs configuration, +files in2 and/or in3 do not exist. Values range are from 0 to 255 for single +ended inputs and -128 to +127 for differential inputs (8-bit ADC). + +The out0_enable file is RW. Reading gives "1" for analog output enabled and +"0" for analog output disabled. Writing accepts "0" and "1" accordingly. + +The out0_output file is RW. Writing a number between 0 and 255 (8-bit DAC), send +the value to the digital-to-analog converter. Note that a voltage will +only appears on AOUT pin if aout0_enable equals 1. Reading returns the last +value written. diff --git a/Documentation/i2c/chips/pcf8591 b/Documentation/i2c/chips/pcf8591 deleted file mode 100644 index 5628fcf4207f..000000000000 --- a/Documentation/i2c/chips/pcf8591 +++ /dev/null @@ -1,90 +0,0 @@ -Kernel driver pcf8591 -===================== - -Supported chips: - * Philips PCF8591 - Prefix: 'pcf8591' - Addresses scanned: I2C 0x48 - 0x4f - Datasheet: Publicly available at the Philips Semiconductor website - http://www.semiconductors.philips.com/pip/PCF8591P.html - -Authors: - Aurelien Jarno - valuable contributions by Jan M. Sendler , - Jean Delvare - - -Description ------------ -The PCF8591 is an 8-bit A/D and D/A converter (4 analog inputs and one -analog output) for the I2C bus produced by Philips Semiconductors. It -is designed to provide a byte I2C interface to up to 4 separate devices. - -The PCF8591 has 4 analog inputs programmable as single-ended or -differential inputs : -- mode 0 : four single ended inputs - Pins AIN0 to AIN3 are single ended inputs for channels 0 to 3 - -- mode 1 : three differential inputs - Pins AIN3 is the common negative differential input - Pins AIN0 to AIN2 are positive differential inputs for channels 0 to 2 - -- mode 2 : single ended and differential mixed - Pins AIN0 and AIN1 are single ended inputs for channels 0 and 1 - Pins AIN2 is the positive differential input for channel 3 - Pins AIN3 is the negative differential input for channel 3 - -- mode 3 : two differential inputs - Pins AIN0 is the positive differential input for channel 0 - Pins AIN1 is the negative differential input for channel 0 - Pins AIN2 is the positive differential input for channel 1 - Pins AIN3 is the negative differential input for channel 1 - -See the datasheet for details. - -Module parameters ------------------ - -* input_mode int - - Analog input mode: - 0 = four single ended inputs - 1 = three differential inputs - 2 = single ended and differential mixed - 3 = two differential inputs - - -Accessing PCF8591 via /sys interface -------------------------------------- - -! Be careful ! -The PCF8591 is plainly impossible to detect ! Stupid chip. -So every chip with address in the interval [48..4f] is -detected as PCF8591. If you have other chips in this address -range, the workaround is to load this module after the one -for your others chips. - -On detection (i.e. insmod, modprobe et al.), directories are being -created for each detected PCF8591: - -/sys/bus/devices/<0>-<1>/ -where <0> is the bus the chip was detected on (e. g. i2c-0) -and <1> the chip address ([48..4f]) - -Inside these directories, there are such files: -in0, in1, in2, in3, out0_enable, out0_output, name - -Name contains chip name. - -The in0, in1, in2 and in3 files are RO. Reading gives the value of the -corresponding channel. Depending on the current analog inputs configuration, -files in2 and/or in3 do not exist. Values range are from 0 to 255 for single -ended inputs and -128 to +127 for differential inputs (8-bit ADC). - -The out0_enable file is RW. Reading gives "1" for analog output enabled and -"0" for analog output disabled. Writing accepts "0" and "1" accordingly. - -The out0_output file is RW. Writing a number between 0 and 255 (8-bit DAC), send -the value to the digital-to-analog converter. Note that a voltage will -only appears on AOUT pin if aout0_enable equals 1. Reading returns the last -value written. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 0eb4170cc4af..9a22816b37dd 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -635,6 +635,20 @@ config SENSORS_PC87427 This driver can also be built as a module. If so, the module will be called pc87427. +config SENSORS_PCF8591 + tristate "Philips PCF8591 ADC/DAC" + depends on I2C + default n + help + If you say yes here you get support for Philips PCF8591 4-channel + ADC, 1-channel DAC chips. + + This driver can also be built as a module. If so, the module + will be called pcf8591. + + These devices are hard to detect and rarely found on mainstream + hardware. If unsure, say N. + config SENSORS_SIS5595 tristate "Silicon Integrated Systems Corp. SiS5595" depends on PCI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 2e80f37f39eb..e332d6267920 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o +obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c new file mode 100644 index 000000000000..1d7ffebd679d --- /dev/null +++ b/drivers/hwmon/pcf8591.c @@ -0,0 +1,325 @@ +/* + Copyright (C) 2001-2004 Aurelien Jarno + Ported to Linux 2.6 by Aurelien Jarno with + the help of Jean Delvare + + 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 +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(pcf8591); + +static int input_mode; +module_param(input_mode, int, 0); +MODULE_PARM_DESC(input_mode, + "Analog input mode:\n" + " 0 = four single ended inputs\n" + " 1 = three differential inputs\n" + " 2 = single ended and differential mixed\n" + " 3 = two differential inputs\n"); + +/* The PCF8591 control byte + 7 6 5 4 3 2 1 0 + | 0 |AOEF| AIP | 0 |AINC| AICH | */ + +/* Analog Output Enable Flag (analog output active if 1) */ +#define PCF8591_CONTROL_AOEF 0x40 + +/* Analog Input Programming + 0x00 = four single ended inputs + 0x10 = three differential inputs + 0x20 = single ended and differential mixed + 0x30 = two differential inputs */ +#define PCF8591_CONTROL_AIP_MASK 0x30 + +/* Autoincrement Flag (switch on if 1) */ +#define PCF8591_CONTROL_AINC 0x04 + +/* Channel selection + 0x00 = channel 0 + 0x01 = channel 1 + 0x02 = channel 2 + 0x03 = channel 3 */ +#define PCF8591_CONTROL_AICH_MASK 0x03 + +/* Initial values */ +#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF) +#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ + +/* Conversions */ +#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) + +struct pcf8591_data { + struct mutex update_lock; + + u8 control; + u8 aout; +}; + +static void pcf8591_init_client(struct i2c_client *client); +static int pcf8591_read_channel(struct device *dev, int channel); + +/* following are the sysfs callback functions */ +#define show_in_channel(channel) \ +static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\ +} \ +static DEVICE_ATTR(in##channel##_input, S_IRUGO, \ + show_in##channel##_input, NULL); + +show_in_channel(0); +show_in_channel(1); +show_in_channel(2); +show_in_channel(3); + +static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return sprintf(buf, "%d\n", data->aout * 10); +} + +static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int value; + struct i2c_client *client = to_i2c_client(dev); + struct pcf8591_data *data = i2c_get_clientdata(client); + if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) { + data->aout = value; + i2c_smbus_write_byte_data(client, data->control, data->aout); + return count; + } + return -EINVAL; +} + +static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, + show_out0_ouput, set_out0_output); + +static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF))); +} + +static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pcf8591_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + mutex_lock(&data->update_lock); + if (val) + data->control |= PCF8591_CONTROL_AOEF; + else + data->control &= ~PCF8591_CONTROL_AOEF; + i2c_smbus_write_byte(client, data->control); + mutex_unlock(&data->update_lock); + return count; +} + +static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, + show_out0_enable, set_out0_enable); + +static struct attribute *pcf8591_attributes[] = { + &dev_attr_out0_enable.attr, + &dev_attr_out0_output.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL +}; + +static const struct attribute_group pcf8591_attr_group = { + .attrs = pcf8591_attributes, +}; + +static struct attribute *pcf8591_attributes_opt[] = { + &dev_attr_in2_input.attr, + &dev_attr_in3_input.attr, + NULL +}; + +static const struct attribute_group pcf8591_attr_group_opt = { + .attrs = pcf8591_attributes_opt, +}; + +/* + * Real code + */ + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int pcf8591_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE + | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + /* Now, we would do the remaining detection. But the PCF8591 is plainly + impossible to detect! Stupid chip. */ + + strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); + + return 0; +} + +static int pcf8591_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf8591_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the PCF8591 chip */ + pcf8591_init_client(client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group); + if (err) + goto exit_kfree; + + /* Register input2 if not in "two differential inputs" mode */ + if (input_mode != 3) { + if ((err = device_create_file(&client->dev, + &dev_attr_in2_input))) + goto exit_sysfs_remove; + } + + /* Register input3 only in "four single ended inputs" mode */ + if (input_mode == 0) { + if ((err = device_create_file(&client->dev, + &dev_attr_in3_input))) + goto exit_sysfs_remove; + } + + return 0; + +exit_sysfs_remove: + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); +exit_kfree: + kfree(data); +exit: + return err; +} + +static int pcf8591_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); + sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); + kfree(i2c_get_clientdata(client)); + return 0; +} + +/* Called when we have found a new PCF8591. */ +static void pcf8591_init_client(struct i2c_client *client) +{ + struct pcf8591_data *data = i2c_get_clientdata(client); + data->control = PCF8591_INIT_CONTROL; + data->aout = PCF8591_INIT_AOUT; + + i2c_smbus_write_byte_data(client, data->control, data->aout); + + /* The first byte transmitted contains the conversion code of the + previous read cycle. FLUSH IT! */ + i2c_smbus_read_byte(client); +} + +static int pcf8591_read_channel(struct device *dev, int channel) +{ + u8 value; + struct i2c_client *client = to_i2c_client(dev); + struct pcf8591_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) { + data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK) + | channel; + i2c_smbus_write_byte(client, data->control); + + /* The first byte transmitted contains the conversion code of + the previous read cycle. FLUSH IT! */ + i2c_smbus_read_byte(client); + } + value = i2c_smbus_read_byte(client); + + mutex_unlock(&data->update_lock); + + if ((channel == 2 && input_mode == 2) || + (channel != 3 && (input_mode == 1 || input_mode == 3))) + return (10 * REG_TO_SIGNED(value)); + else + return (10 * value); +} + +static const struct i2c_device_id pcf8591_id[] = { + { "pcf8591", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8591_id); + +static struct i2c_driver pcf8591_driver = { + .driver = { + .name = "pcf8591", + }, + .probe = pcf8591_probe, + .remove = pcf8591_remove, + .id_table = pcf8591_id, + + .class = I2C_CLASS_HWMON, /* Nearest choice */ + .detect = pcf8591_detect, + .address_data = &addr_data, +}; + +static int __init pcf8591_init(void) +{ + if (input_mode < 0 || input_mode > 3) { + printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n", + input_mode); + input_mode = 0; + } + return i2c_add_driver(&pcf8591_driver); +} + +static void __exit pcf8591_exit(void) +{ + i2c_del_driver(&pcf8591_driver); +} + +MODULE_AUTHOR("Aurelien Jarno "); +MODULE_DESCRIPTION("PCF8591 driver"); +MODULE_LICENSE("GPL"); + +module_init(pcf8591_init); +module_exit(pcf8591_exit); diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index c80312c1f382..8f8c81eb0aee 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -64,19 +64,6 @@ config SENSORS_PCA9539 This driver is deprecated and will be dropped soon. Use drivers/gpio/pca953x.c instead. -config SENSORS_PCF8591 - tristate "Philips PCF8591" - depends on EXPERIMENTAL - default n - help - If you say yes here you get support for Philips PCF8591 chips. - - This driver can also be built as a module. If so, the module - will be called pcf8591. - - These devices are hard to detect and rarely found on mainstream - hardware. If unsure, say N. - config SENSORS_MAX6875 tristate "Maxim MAX6875 Power supply supervisor" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index d142f238a2de..55a376037183 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_SENSORS_MAX6875) += max6875.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_PCF8575) += pcf8575.o -obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) diff --git a/drivers/i2c/chips/pcf8591.c b/drivers/i2c/chips/pcf8591.c deleted file mode 100644 index 16ce3e193776..000000000000 --- a/drivers/i2c/chips/pcf8591.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - Copyright (C) 2001-2004 Aurelien Jarno - Ported to Linux 2.6 by Aurelien Jarno with - the help of Jean Delvare - - 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 -#include -#include -#include -#include - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_1(pcf8591); - -static int input_mode; -module_param(input_mode, int, 0); -MODULE_PARM_DESC(input_mode, - "Analog input mode:\n" - " 0 = four single ended inputs\n" - " 1 = three differential inputs\n" - " 2 = single ended and differential mixed\n" - " 3 = two differential inputs\n"); - -/* The PCF8591 control byte - 7 6 5 4 3 2 1 0 - | 0 |AOEF| AIP | 0 |AINC| AICH | */ - -/* Analog Output Enable Flag (analog output active if 1) */ -#define PCF8591_CONTROL_AOEF 0x40 - -/* Analog Input Programming - 0x00 = four single ended inputs - 0x10 = three differential inputs - 0x20 = single ended and differential mixed - 0x30 = two differential inputs */ -#define PCF8591_CONTROL_AIP_MASK 0x30 - -/* Autoincrement Flag (switch on if 1) */ -#define PCF8591_CONTROL_AINC 0x04 - -/* Channel selection - 0x00 = channel 0 - 0x01 = channel 1 - 0x02 = channel 2 - 0x03 = channel 3 */ -#define PCF8591_CONTROL_AICH_MASK 0x03 - -/* Initial values */ -#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF) -#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ - -/* Conversions */ -#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) - -struct pcf8591_data { - struct mutex update_lock; - - u8 control; - u8 aout; -}; - -static void pcf8591_init_client(struct i2c_client *client); -static int pcf8591_read_channel(struct device *dev, int channel); - -/* following are the sysfs callback functions */ -#define show_in_channel(channel) \ -static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\ -} \ -static DEVICE_ATTR(in##channel##_input, S_IRUGO, \ - show_in##channel##_input, NULL); - -show_in_channel(0); -show_in_channel(1); -show_in_channel(2); -show_in_channel(3); - -static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); - return sprintf(buf, "%d\n", data->aout * 10); -} - -static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned int value; - struct i2c_client *client = to_i2c_client(dev); - struct pcf8591_data *data = i2c_get_clientdata(client); - if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) { - data->aout = value; - i2c_smbus_write_byte_data(client, data->control, data->aout); - return count; - } - return -EINVAL; -} - -static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, - show_out0_ouput, set_out0_output); - -static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); - return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF))); -} - -static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf8591_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - mutex_lock(&data->update_lock); - if (val) - data->control |= PCF8591_CONTROL_AOEF; - else - data->control &= ~PCF8591_CONTROL_AOEF; - i2c_smbus_write_byte(client, data->control); - mutex_unlock(&data->update_lock); - return count; -} - -static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, - show_out0_enable, set_out0_enable); - -static struct attribute *pcf8591_attributes[] = { - &dev_attr_out0_enable.attr, - &dev_attr_out0_output.attr, - &dev_attr_in0_input.attr, - &dev_attr_in1_input.attr, - NULL -}; - -static const struct attribute_group pcf8591_attr_group = { - .attrs = pcf8591_attributes, -}; - -static struct attribute *pcf8591_attributes_opt[] = { - &dev_attr_in2_input.attr, - &dev_attr_in3_input.attr, - NULL -}; - -static const struct attribute_group pcf8591_attr_group_opt = { - .attrs = pcf8591_attributes_opt, -}; - -/* - * Real code - */ - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int pcf8591_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE - | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; - - /* Now, we would do the remaining detection. But the PCF8591 is plainly - impossible to detect! Stupid chip. */ - - strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); - - return 0; -} - -static int pcf8591_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pcf8591_data *data; - int err; - - if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - - /* Initialize the PCF8591 chip */ - pcf8591_init_client(client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group); - if (err) - goto exit_kfree; - - /* Register input2 if not in "two differential inputs" mode */ - if (input_mode != 3) { - if ((err = device_create_file(&client->dev, - &dev_attr_in2_input))) - goto exit_sysfs_remove; - } - - /* Register input3 only in "four single ended inputs" mode */ - if (input_mode == 0) { - if ((err = device_create_file(&client->dev, - &dev_attr_in3_input))) - goto exit_sysfs_remove; - } - - return 0; - -exit_sysfs_remove: - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); -exit_kfree: - kfree(data); -exit: - return err; -} - -static int pcf8591_remove(struct i2c_client *client) -{ - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); - sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); - kfree(i2c_get_clientdata(client)); - return 0; -} - -/* Called when we have found a new PCF8591. */ -static void pcf8591_init_client(struct i2c_client *client) -{ - struct pcf8591_data *data = i2c_get_clientdata(client); - data->control = PCF8591_INIT_CONTROL; - data->aout = PCF8591_INIT_AOUT; - - i2c_smbus_write_byte_data(client, data->control, data->aout); - - /* The first byte transmitted contains the conversion code of the - previous read cycle. FLUSH IT! */ - i2c_smbus_read_byte(client); -} - -static int pcf8591_read_channel(struct device *dev, int channel) -{ - u8 value; - struct i2c_client *client = to_i2c_client(dev); - struct pcf8591_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) { - data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK) - | channel; - i2c_smbus_write_byte(client, data->control); - - /* The first byte transmitted contains the conversion code of - the previous read cycle. FLUSH IT! */ - i2c_smbus_read_byte(client); - } - value = i2c_smbus_read_byte(client); - - mutex_unlock(&data->update_lock); - - if ((channel == 2 && input_mode == 2) || - (channel != 3 && (input_mode == 1 || input_mode == 3))) - return (10 * REG_TO_SIGNED(value)); - else - return (10 * value); -} - -static const struct i2c_device_id pcf8591_id[] = { - { "pcf8591", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pcf8591_id); - -static struct i2c_driver pcf8591_driver = { - .driver = { - .name = "pcf8591", - }, - .probe = pcf8591_probe, - .remove = pcf8591_remove, - .id_table = pcf8591_id, - - .class = I2C_CLASS_HWMON, /* Nearest choice */ - .detect = pcf8591_detect, - .address_data = &addr_data, -}; - -static int __init pcf8591_init(void) -{ - if (input_mode < 0 || input_mode > 3) { - printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n", - input_mode); - input_mode = 0; - } - return i2c_add_driver(&pcf8591_driver); -} - -static void __exit pcf8591_exit(void) -{ - i2c_del_driver(&pcf8591_driver); -} - -MODULE_AUTHOR("Aurelien Jarno "); -MODULE_DESCRIPTION("PCF8591 driver"); -MODULE_LICENSE("GPL"); - -module_init(pcf8591_init); -module_exit(pcf8591_exit); -- cgit v1.2.3-59-g8ed1b From ec19920944246b4686c7772a58507a20c361dc9d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 30 Mar 2009 21:46:44 +0200 Subject: hwmon: Define a standard interface for chassis intrusion detection Define a standard interface for the chassis intrusion detection feature some hardware monitoring chips have. Some drivers have custom sysfs entries for it, but a standard interface would allow integration with user-space (namely libsensors.) Signed-off-by: Jean Delvare Acked-by: Hans de Goede Acked-by: Matt Roberds --- Documentation/hwmon/sysfs-interface | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 6dbfd5efd991..2f10ce6a879f 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -365,6 +365,7 @@ energy[1-*]_input Cumulative energy use Unit: microJoule RO + ********** * Alarms * ********** @@ -453,6 +454,27 @@ beep_mask Bitmask for beep. RW +*********************** +* Intrusion detection * +*********************** + +intrusion[0-*]_alarm + Chassis intrusion detection + 0: OK + 1: intrusion detected + RW + Contrary to regular alarm flags which clear themselves + automatically when read, this one sticks until cleared by + the user. This is done by writing 0 to the file. Writing + other values is unsupported. + +intrusion[0-*]_beep + Chassis intrusion beep + 0: disable + 1: enable + RW + + sysfs attribute writes interpretation ------------------------------------- -- cgit v1.2.3-59-g8ed1b From 99b76233803beab302123d243eea9e41149804f3 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 25 Mar 2009 22:48:06 +0300 Subject: proc 2/2: remove struct proc_dir_entry::owner Setting ->owner as done currently (pde->owner = THIS_MODULE) is racy as correctly noted at bug #12454. Someone can lookup entry with NULL ->owner, thus not pinning enything, and release it later resulting in module refcount underflow. We can keep ->owner and supply it at registration time like ->proc_fops and ->data. But this leaves ->owner as easy-manipulative field (just one C assignment) and somebody will forget to unpin previous/pin current module when switching ->owner. ->proc_fops is declared as "const" which should give some thoughts. ->read_proc/->write_proc were just fixed to not require ->owner for protection. rmmod'ed directories will be empty and return "." and ".." -- no harm. And directories with tricky enough readdir and lookup shouldn't be modular. We definitely don't want such modular code. Removing ->owner will also make PDE smaller. So, let's nuke it. Kudos to Jeff Layton for reminding about this, let's say, oversight. http://bugzilla.kernel.org/show_bug.cgi?id=12454 Signed-off-by: Alexey Dobriyan --- Documentation/DocBook/procfs_example.c | 9 --------- arch/alpha/kernel/srm_env.c | 5 ----- arch/blackfin/mm/sram-alloc.c | 1 - arch/ia64/kernel/palinfo.c | 2 -- arch/ia64/sn/kernel/sn2/prominfo_proc.c | 9 ++------- arch/powerpc/kernel/rtas_flash.c | 1 - arch/sparc/kernel/led.c | 1 - arch/x86/kernel/cpu/mtrr/if.c | 10 +--------- drivers/acpi/ac.c | 1 - drivers/acpi/battery.c | 1 - drivers/acpi/button.c | 3 --- drivers/acpi/fan.c | 2 -- drivers/acpi/processor_core.c | 2 -- drivers/acpi/sbs.c | 1 - drivers/acpi/thermal.c | 2 -- drivers/acpi/video.c | 5 ----- drivers/block/ps3vram.c | 2 -- drivers/char/ipmi/ipmi_msghandler.c | 12 ++++------- drivers/char/ipmi/ipmi_si_intf.c | 6 +++--- drivers/input/input.c | 2 -- drivers/isdn/hardware/eicon/divasi.c | 1 - drivers/media/video/cpia.c | 4 +--- drivers/message/i2o/i2o_proc.c | 2 -- drivers/net/bonding/bond_main.c | 35 ++------------------------------- drivers/net/irda/vlsi_ir.c | 7 ------- drivers/net/wireless/airo.c | 1 - drivers/platform/x86/asus_acpi.c | 3 --- drivers/platform/x86/thinkpad_acpi.c | 2 -- drivers/platform/x86/toshiba_acpi.c | 3 --- drivers/rtc/rtc-proc.c | 10 ++-------- drivers/s390/block/dasd_proc.c | 2 -- drivers/scsi/scsi_devinfo.c | 2 -- drivers/scsi/scsi_proc.c | 3 --- drivers/video/via/viafbdev.c | 5 ----- fs/afs/proc.c | 1 - fs/cifs/cifs_debug.c | 1 - fs/jfs/jfs_debug.c | 1 - fs/nfs/client.c | 2 -- fs/proc/inode.c | 19 +++--------------- fs/proc/proc_tty.c | 1 - fs/reiserfs/procfs.c | 5 +---- include/linux/ipmi_smi.h | 2 +- include/linux/proc_fs.h | 4 ---- net/appletalk/atalk_proc.c | 1 - net/atm/mpoa_proc.c | 1 - net/atm/proc.c | 1 - net/can/bcm.c | 4 ---- net/can/proc.c | 2 -- net/core/pktgen.c | 1 - net/irda/irproc.c | 1 - net/llc/llc_proc.c | 1 - net/sctp/protocol.c | 8 ++------ net/sunrpc/cache.c | 4 ---- net/sunrpc/stats.c | 10 ++-------- sound/core/info.c | 31 ++--------------------------- 55 files changed, 26 insertions(+), 232 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/procfs_example.c b/Documentation/DocBook/procfs_example.c index 8c6396e4bf31..a5b11793b1e0 100644 --- a/Documentation/DocBook/procfs_example.c +++ b/Documentation/DocBook/procfs_example.c @@ -117,9 +117,6 @@ static int __init init_procfs_example(void) rv = -ENOMEM; goto out; } - - example_dir->owner = THIS_MODULE; - /* create jiffies using convenience function */ jiffies_file = create_proc_read_entry("jiffies", 0444, example_dir, @@ -130,8 +127,6 @@ static int __init init_procfs_example(void) goto no_jiffies; } - jiffies_file->owner = THIS_MODULE; - /* create foo and bar files using same callback * functions */ @@ -146,7 +141,6 @@ static int __init init_procfs_example(void) foo_file->data = &foo_data; foo_file->read_proc = proc_read_foobar; foo_file->write_proc = proc_write_foobar; - foo_file->owner = THIS_MODULE; bar_file = create_proc_entry("bar", 0644, example_dir); if(bar_file == NULL) { @@ -159,7 +153,6 @@ static int __init init_procfs_example(void) bar_file->data = &bar_data; bar_file->read_proc = proc_read_foobar; bar_file->write_proc = proc_write_foobar; - bar_file->owner = THIS_MODULE; /* create symlink */ symlink = proc_symlink("jiffies_too", example_dir, @@ -169,8 +162,6 @@ static int __init init_procfs_example(void) goto no_symlink; } - symlink->owner = THIS_MODULE; - /* everything OK */ printk(KERN_INFO "%s %s initialised\n", MODULE_NAME, MODULE_VERS); diff --git a/arch/alpha/kernel/srm_env.c b/arch/alpha/kernel/srm_env.c index 78ad7cd1bbd6..d12af472e1c0 100644 --- a/arch/alpha/kernel/srm_env.c +++ b/arch/alpha/kernel/srm_env.c @@ -218,7 +218,6 @@ srm_env_init(void) BASE_DIR); goto cleanup; } - base_dir->owner = THIS_MODULE; /* * Create per-name subdirectory @@ -229,7 +228,6 @@ srm_env_init(void) BASE_DIR, NAMED_DIR); goto cleanup; } - named_dir->owner = THIS_MODULE; /* * Create per-number subdirectory @@ -241,7 +239,6 @@ srm_env_init(void) goto cleanup; } - numbered_dir->owner = THIS_MODULE; /* * Create all named nodes @@ -254,7 +251,6 @@ srm_env_init(void) goto cleanup; entry->proc_entry->data = (void *) entry; - entry->proc_entry->owner = THIS_MODULE; entry->proc_entry->read_proc = srm_env_read; entry->proc_entry->write_proc = srm_env_write; @@ -275,7 +271,6 @@ srm_env_init(void) entry->id = var_num; entry->proc_entry->data = (void *) entry; - entry->proc_entry->owner = THIS_MODULE; entry->proc_entry->read_proc = srm_env_read; entry->proc_entry->write_proc = srm_env_write; } diff --git a/arch/blackfin/mm/sram-alloc.c b/arch/blackfin/mm/sram-alloc.c index 834cab7438a8..530d1393a232 100644 --- a/arch/blackfin/mm/sram-alloc.c +++ b/arch/blackfin/mm/sram-alloc.c @@ -854,7 +854,6 @@ static int __init sram_proc_init(void) printk(KERN_WARNING "unable to create /proc/sram\n"); return -1; } - ptr->owner = THIS_MODULE; ptr->read_proc = sram_proc_read; return 0; } diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c index e5c57f413ca2..a4f19c70aadd 100644 --- a/arch/ia64/kernel/palinfo.c +++ b/arch/ia64/kernel/palinfo.c @@ -1002,8 +1002,6 @@ create_palinfo_proc_entries(unsigned int cpu) *pdir = create_proc_read_entry( palinfo_entries[j].name, 0, cpu_dir, palinfo_read_entry, (void *)f.value); - if (*pdir) - (*pdir)->owner = THIS_MODULE; pdir++; } } diff --git a/arch/ia64/sn/kernel/sn2/prominfo_proc.c b/arch/ia64/sn/kernel/sn2/prominfo_proc.c index 4dcce3d0e04c..e63328818643 100644 --- a/arch/ia64/sn/kernel/sn2/prominfo_proc.c +++ b/arch/ia64/sn/kernel/sn2/prominfo_proc.c @@ -225,7 +225,6 @@ static struct proc_dir_entry *sgi_prominfo_entry; int __init prominfo_init(void) { struct proc_dir_entry **entp; - struct proc_dir_entry *p; cnodeid_t cnodeid; unsigned long nasid; int size; @@ -246,14 +245,10 @@ int __init prominfo_init(void) sprintf(name, "node%d", cnodeid); *entp = proc_mkdir(name, sgi_prominfo_entry); nasid = cnodeid_to_nasid(cnodeid); - p = create_proc_read_entry("fit", 0, *entp, read_fit_entry, + create_proc_read_entry("fit", 0, *entp, read_fit_entry, (void *)nasid); - if (p) - p->owner = THIS_MODULE; - p = create_proc_read_entry("version", 0, *entp, + create_proc_read_entry("version", 0, *entp, read_version_entry, (void *)nasid); - if (p) - p->owner = THIS_MODULE; entp++; } diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 149cb112cd1a..13011a96a977 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c @@ -669,7 +669,6 @@ static void remove_flash_pde(struct proc_dir_entry *dp) { if (dp) { kfree(dp->data); - dp->owner = NULL; remove_proc_entry(dp->name, dp->parent); } } diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c index adaaed4ea2fb..00d034ea2164 100644 --- a/arch/sparc/kernel/led.c +++ b/arch/sparc/kernel/led.c @@ -126,7 +126,6 @@ static int __init led_init(void) led = proc_create("led", 0, NULL, &led_proc_fops); if (!led) return -ENOMEM; - led->owner = THIS_MODULE; printk(KERN_INFO "led: version %s, Lars Kotthoff \n", diff --git a/arch/x86/kernel/cpu/mtrr/if.c b/arch/x86/kernel/cpu/mtrr/if.c index 4c4214690dd1..fb73a52913a4 100644 --- a/arch/x86/kernel/cpu/mtrr/if.c +++ b/arch/x86/kernel/cpu/mtrr/if.c @@ -377,10 +377,6 @@ static const struct file_operations mtrr_fops = { .release = mtrr_close, }; - -static struct proc_dir_entry *proc_root_mtrr; - - static int mtrr_seq_show(struct seq_file *seq, void *offset) { char factor; @@ -423,11 +419,7 @@ static int __init mtrr_if_init(void) (!cpu_has(c, X86_FEATURE_CENTAUR_MCR))) return -ENODEV; - proc_root_mtrr = - proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops); - - if (proc_root_mtrr) - proc_root_mtrr->owner = THIS_MODULE; + proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops); return 0; } diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 9b917dac7732..88e42abf5d88 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -191,7 +191,6 @@ static int acpi_ac_add_fs(struct acpi_device *device) acpi_ac_dir); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; } /* 'state' [R] */ diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 69cbc57c2d1c..3bcb5bfc45d3 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -760,7 +760,6 @@ static int acpi_battery_add_fs(struct acpi_device *device) acpi_battery_dir); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; } for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) { diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 171fd914f435..c2f06069dcd4 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -200,12 +200,10 @@ static int acpi_button_add_fs(struct acpi_device *device) if (!entry) return -ENODEV; - entry->owner = THIS_MODULE; acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; /* 'info' [R] */ entry = proc_create_data(ACPI_BUTTON_FILE_INFO, @@ -522,7 +520,6 @@ static int __init acpi_button_init(void) acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); if (!acpi_button_dir) return -ENODEV; - acpi_button_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_button_driver); if (result < 0) { remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index eaaee1660bdf..8a02944bf92d 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -193,7 +193,6 @@ static int acpi_fan_add_fs(struct acpi_device *device) acpi_fan_dir); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; } /* 'status' [R/W] */ @@ -347,7 +346,6 @@ static int __init acpi_fan_init(void) acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir); if (!acpi_fan_dir) return -ENODEV; - acpi_fan_dir->owner = THIS_MODULE; #endif result = acpi_bus_register_driver(&acpi_fan_driver); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 0cc2fd31e376..fa2f7422d23d 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -359,7 +359,6 @@ static int acpi_processor_add_fs(struct acpi_device *device) if (!acpi_device_dir(device)) return -ENODEV; } - acpi_device_dir(device)->owner = THIS_MODULE; /* 'info' [R] */ entry = proc_create_data(ACPI_PROCESSOR_FILE_INFO, @@ -1137,7 +1136,6 @@ static int __init acpi_processor_init(void) acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir); if (!acpi_processor_dir) return -ENOMEM; - acpi_processor_dir->owner = THIS_MODULE; /* * Check whether the system is DMI table. If yes, OSPM diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 6050ce481873..59afd52ccc12 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -488,7 +488,6 @@ acpi_sbs_add_fs(struct proc_dir_entry **dir, if (!*dir) { return -ENODEV; } - (*dir)->owner = THIS_MODULE; } /* 'info' [R] */ diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 99e6f1f8ea45..c11f9aeca706 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -1506,7 +1506,6 @@ static int acpi_thermal_add_fs(struct acpi_device *device) acpi_thermal_dir); if (!acpi_device_dir(device)) return -ENODEV; - acpi_device_dir(device)->owner = THIS_MODULE; } /* 'state' [R] */ @@ -1875,7 +1874,6 @@ static int __init acpi_thermal_init(void) acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); if (!acpi_thermal_dir) return -ENODEV; - acpi_thermal_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_thermal_driver); if (result < 0) { diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index bb5ed059114a..67cc36dc9b82 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1125,8 +1125,6 @@ static int acpi_video_device_add_fs(struct acpi_device *device) if (!device_dir) return -ENOMEM; - device_dir->owner = THIS_MODULE; - /* 'info' [R] */ entry = proc_create_data("info", S_IRUGO, device_dir, &acpi_video_device_info_fops, acpi_driver_data(device)); @@ -1403,8 +1401,6 @@ static int acpi_video_bus_add_fs(struct acpi_device *device) if (!device_dir) return -ENOMEM; - device_dir->owner = THIS_MODULE; - /* 'info' [R] */ entry = proc_create_data("info", S_IRUGO, device_dir, &acpi_video_bus_info_fops, @@ -2131,7 +2127,6 @@ static int __init acpi_video_init(void) acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); if (!acpi_video_dir) return -ENODEV; - acpi_video_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_video_bus); if (result < 0) { diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index 393ed6760d78..8eddef373a91 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -551,8 +551,6 @@ static void __devinit ps3vram_proc_init(struct ps3_system_bus_device *dev) dev_warn(&dev->core, "failed to create /proc entry\n"); return; } - - pde->owner = THIS_MODULE; pde->data = priv; } diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 7a88dfd4427b..e93fc8d22fb2 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1944,7 +1944,7 @@ static int stat_file_read_proc(char *page, char **start, off_t off, int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, read_proc_t *read_proc, - void *data, struct module *owner) + void *data) { int rv = 0; #ifdef CONFIG_PROC_FS @@ -1970,7 +1970,6 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, } else { file->data = data; file->read_proc = read_proc; - file->owner = owner; mutex_lock(&smi->proc_entry_lock); /* Stick it on the list. */ @@ -1993,23 +1992,21 @@ static int add_proc_entries(ipmi_smi_t smi, int num) smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) rv = -ENOMEM; - else - smi->proc_dir->owner = THIS_MODULE; if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "stats", stat_file_read_proc, - smi, THIS_MODULE); + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "ipmb", ipmb_file_read_proc, - smi, THIS_MODULE); + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "version", version_file_read_proc, - smi, THIS_MODULE); + smi); #endif /* CONFIG_PROC_FS */ return rv; @@ -4265,7 +4262,6 @@ static int ipmi_init_msghandler(void) return -ENOMEM; } - proc_ipmi_root->owner = THIS_MODULE; #endif /* CONFIG_PROC_FS */ setup_timer(&ipmi_timer, ipmi_timeout, 0); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 3000135f2ead..e58ea4cd55ce 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2899,7 +2899,7 @@ static int try_smi_init(struct smi_info *new_smi) rv = ipmi_smi_add_proc_entry(new_smi->intf, "type", type_file_read_proc, - new_smi, THIS_MODULE); + new_smi); if (rv) { printk(KERN_ERR "ipmi_si: Unable to create proc entry: %d\n", @@ -2909,7 +2909,7 @@ static int try_smi_init(struct smi_info *new_smi) rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats", stat_file_read_proc, - new_smi, THIS_MODULE); + new_smi); if (rv) { printk(KERN_ERR "ipmi_si: Unable to create proc entry: %d\n", @@ -2919,7 +2919,7 @@ static int try_smi_init(struct smi_info *new_smi) rv = ipmi_smi_add_proc_entry(new_smi->intf, "params", param_read_proc, - new_smi, THIS_MODULE); + new_smi); if (rv) { printk(KERN_ERR "ipmi_si: Unable to create proc entry: %d\n", diff --git a/drivers/input/input.c b/drivers/input/input.c index 1730d7331a5d..ec3db3ade118 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -903,8 +903,6 @@ static int __init input_proc_init(void) if (!proc_bus_input_dir) return -ENOMEM; - proc_bus_input_dir->owner = THIS_MODULE; - entry = proc_create("devices", 0, proc_bus_input_dir, &input_devices_fileops); if (!entry) diff --git a/drivers/isdn/hardware/eicon/divasi.c b/drivers/isdn/hardware/eicon/divasi.c index f4969fe0a055..69e71ebe7841 100644 --- a/drivers/isdn/hardware/eicon/divasi.c +++ b/drivers/isdn/hardware/eicon/divasi.c @@ -118,7 +118,6 @@ static int DIVA_INIT_FUNCTION create_um_idi_proc(void) return (0); um_idi_proc_entry->read_proc = um_idi_proc_read; - um_idi_proc_entry->owner = THIS_MODULE; return (1); } diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index c3b0c8c63c76..43ab0adf3b61 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -1381,9 +1381,7 @@ static void proc_cpia_create(void) { cpia_proc_root = proc_mkdir("cpia", NULL); - if (cpia_proc_root) - cpia_proc_root->owner = THIS_MODULE; - else + if (!cpia_proc_root) LOG("Unable to initialise /proc/cpia\n"); } diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c index 9a36b5a7de57..7045c45da9b1 100644 --- a/drivers/message/i2o/i2o_proc.c +++ b/drivers/message/i2o/i2o_proc.c @@ -2037,8 +2037,6 @@ static int __init i2o_proc_fs_create(void) if (!i2o_proc_dir_root) return -1; - i2o_proc_dir_root->owner = THIS_MODULE; - list_for_each_entry(c, &i2o_controllers, list) i2o_proc_iop_add(i2o_proc_dir_root, c); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 9c326a50a3ee..99610f358c40 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3444,25 +3444,12 @@ static void bond_remove_proc_entry(struct bonding *bond) */ static void bond_create_proc_dir(void) { - int len = strlen(DRV_NAME); - - for (bond_proc_dir = init_net.proc_net->subdir; bond_proc_dir; - bond_proc_dir = bond_proc_dir->next) { - if ((bond_proc_dir->namelen == len) && - !memcmp(bond_proc_dir->name, DRV_NAME, len)) { - break; - } - } - if (!bond_proc_dir) { bond_proc_dir = proc_mkdir(DRV_NAME, init_net.proc_net); - if (bond_proc_dir) { - bond_proc_dir->owner = THIS_MODULE; - } else { + if (!bond_proc_dir) printk(KERN_WARNING DRV_NAME ": Warning: cannot create /proc/net/%s\n", DRV_NAME); - } } } @@ -3471,25 +3458,7 @@ static void bond_create_proc_dir(void) */ static void bond_destroy_proc_dir(void) { - struct proc_dir_entry *de; - - if (!bond_proc_dir) { - return; - } - - /* verify that the /proc dir is empty */ - for (de = bond_proc_dir->subdir; de; de = de->next) { - /* ignore . and .. */ - if (*(de->name) != '.') { - break; - } - } - - if (de) { - if (bond_proc_dir->owner == THIS_MODULE) { - bond_proc_dir->owner = NULL; - } - } else { + if (bond_proc_dir) { remove_proc_entry(DRV_NAME, init_net.proc_net); bond_proc_dir = NULL; } diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index 1243bc8e0035..ac0e4b6b6b66 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -1871,13 +1871,6 @@ static int __init vlsi_mod_init(void) * without procfs - it's not required for the driver to work. */ vlsi_proc_root = proc_mkdir(PROC_DIR, NULL); - if (vlsi_proc_root) { - /* protect registered procdir against module removal. - * Because we are in the module init path there's no race - * window after create_proc_entry (and no barrier needed). - */ - vlsi_proc_root->owner = THIS_MODULE; - } ret = pci_register_driver(&vlsi_irda_driver); diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 7e80aba8a148..31b1cc2b778a 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -4494,7 +4494,6 @@ static int setup_proc_entry( struct net_device *dev, goto fail; apriv->proc_entry->uid = proc_uid; apriv->proc_entry->gid = proc_gid; - apriv->proc_entry->owner = THIS_MODULE; /* Setup the StatsDelta */ entry = proc_create_data("StatsDelta", diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index d63f26e666a4..ba1f7497e4b9 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -987,7 +987,6 @@ asus_proc_add(char *name, proc_writefunc *writefunc, proc->write_proc = writefunc; proc->read_proc = readfunc; proc->data = acpi_driver_data(device); - proc->owner = THIS_MODULE; proc->uid = asus_uid; proc->gid = asus_gid; return 0; @@ -1020,7 +1019,6 @@ static int asus_hotk_add_fs(struct acpi_device *device) if (proc) { proc->read_proc = proc_read_info; proc->data = acpi_driver_data(device); - proc->owner = THIS_MODULE; proc->uid = asus_uid; proc->gid = asus_gid; } else { @@ -1436,7 +1434,6 @@ static int __init asus_acpi_init(void) printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); return -ENODEV; } - asus_proc_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&asus_hotk_driver); if (result < 0) { diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d2433204a40c..3dad27a385d3 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6992,7 +6992,6 @@ static int __init ibm_init(struct ibm_init_struct *iibm) ret = -ENODEV; goto err_out; } - entry->owner = THIS_MODULE; entry->data = ibm; entry->read_proc = &dispatch_procfs_read; if (ibm->write) @@ -7405,7 +7404,6 @@ static int __init thinkpad_acpi_module_init(void) thinkpad_acpi_module_exit(); return -ENODEV; } - proc_dir->owner = THIS_MODULE; ret = platform_driver_register(&tpacpi_pdriver); if (ret) { diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 40e60fc2e596..9f187265db8e 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -679,8 +679,6 @@ static acpi_status __init add_device(void) toshiba_proc_dir, (read_proc_t *) dispatch_read, item); - if (proc) - proc->owner = THIS_MODULE; if (proc && item->write_func) proc->write_proc = (write_proc_t *) dispatch_write; } @@ -772,7 +770,6 @@ static int __init toshiba_acpi_init(void) toshiba_acpi_exit(); return -ENODEV; } else { - toshiba_proc_dir->owner = THIS_MODULE; status = add_device(); if (ACPI_FAILURE(status)) { toshiba_acpi_exit(); diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c index 0c6257a034ff..c086fc30a84c 100644 --- a/drivers/rtc/rtc-proc.c +++ b/drivers/rtc/rtc-proc.c @@ -105,14 +105,8 @@ static const struct file_operations rtc_proc_fops = { void rtc_proc_add_device(struct rtc_device *rtc) { - if (rtc->id == 0) { - struct proc_dir_entry *ent; - - ent = proc_create_data("driver/rtc", 0, NULL, - &rtc_proc_fops, rtc); - if (ent) - ent->owner = rtc->owner; - } + if (rtc->id == 0) + proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc); } void rtc_proc_del_device(struct rtc_device *rtc) diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 2080ba6a69b0..654daa3cdfda 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -320,7 +320,6 @@ dasd_proc_init(void) dasd_proc_root_entry = proc_mkdir("dasd", NULL); if (!dasd_proc_root_entry) goto out_nodasd; - dasd_proc_root_entry->owner = THIS_MODULE; dasd_devices_entry = proc_create("devices", S_IFREG | S_IRUGO | S_IWUSR, dasd_proc_root_entry, @@ -334,7 +333,6 @@ dasd_proc_init(void) goto out_nostatistics; dasd_statistics_entry->read_proc = dasd_statistics_read; dasd_statistics_entry->write_proc = dasd_statistics_write; - dasd_statistics_entry->owner = THIS_MODULE; return 0; out_nostatistics: diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 099b5455bbce..b13481369642 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -596,8 +596,6 @@ int __init scsi_init_devinfo(void) error = -ENOMEM; goto out; } - - p->owner = THIS_MODULE; #endif /* CONFIG_SCSI_PROC_FS */ out: diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index 82f7b2dd08a2..77fbddb507fd 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -115,8 +115,6 @@ void scsi_proc_hostdir_add(struct scsi_host_template *sht) if (!sht->proc_dir) printk(KERN_ERR "%s: proc_mkdir failed for %s\n", __func__, sht->proc_name); - else - sht->proc_dir->owner = sht->module; } mutex_unlock(&global_host_template_mutex); } @@ -163,7 +161,6 @@ void scsi_proc_host_add(struct Scsi_Host *shost) } p->write_proc = proc_scsi_write_proc; - p->owner = sht->module; } /** diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 37b433a08ce8..e327b84820d2 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -2059,25 +2059,21 @@ static void viafb_init_proc(struct proc_dir_entry **viafb_entry) if (viafb_entry) { entry = create_proc_entry("dvp0", 0, *viafb_entry); if (entry) { - entry->owner = THIS_MODULE; entry->read_proc = viafb_dvp0_proc_read; entry->write_proc = viafb_dvp0_proc_write; } entry = create_proc_entry("dvp1", 0, *viafb_entry); if (entry) { - entry->owner = THIS_MODULE; entry->read_proc = viafb_dvp1_proc_read; entry->write_proc = viafb_dvp1_proc_write; } entry = create_proc_entry("dfph", 0, *viafb_entry); if (entry) { - entry->owner = THIS_MODULE; entry->read_proc = viafb_dfph_proc_read; entry->write_proc = viafb_dfph_proc_write; } entry = create_proc_entry("dfpl", 0, *viafb_entry); if (entry) { - entry->owner = THIS_MODULE; entry->read_proc = viafb_dfpl_proc_read; entry->write_proc = viafb_dfpl_proc_write; } @@ -2086,7 +2082,6 @@ static void viafb_init_proc(struct proc_dir_entry **viafb_entry) viaparinfo->chip_info->lvds_chip_info2.lvds_chip_name) { entry = create_proc_entry("vt1636", 0, *viafb_entry); if (entry) { - entry->owner = THIS_MODULE; entry->read_proc = viafb_vt1636_proc_read; entry->write_proc = viafb_vt1636_proc_write; } diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 7578c1ab9e0b..8630615e57fe 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -146,7 +146,6 @@ int afs_proc_init(void) proc_afs = proc_mkdir("fs/afs", NULL); if (!proc_afs) goto error_dir; - proc_afs->owner = THIS_MODULE; p = proc_create("cells", 0, proc_afs, &afs_proc_cells_fops); if (!p) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 877e4d9a1159..7f19fefd3d45 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -404,7 +404,6 @@ cifs_proc_init(void) if (proc_fs_cifs == NULL) return; - proc_fs_cifs->owner = THIS_MODULE; proc_create("DebugData", 0, proc_fs_cifs, &cifs_debug_data_proc_fops); #ifdef CONFIG_CIFS_STATS diff --git a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c index 6a73de84bcef..dd824d9b0b1a 100644 --- a/fs/jfs/jfs_debug.c +++ b/fs/jfs/jfs_debug.c @@ -90,7 +90,6 @@ void jfs_proc_init(void) if (!(base = proc_mkdir("fs/jfs", NULL))) return; - base->owner = THIS_MODULE; for (i = 0; i < NPROCENT; i++) proc_create(Entries[i].name, 0, base, Entries[i].proc_fops); diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 574158ae2398..2277421656e7 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1606,8 +1606,6 @@ int __init nfs_fs_proc_init(void) if (!proc_fs_nfs) goto error_0; - proc_fs_nfs->owner = THIS_MODULE; - /* a file of servers with which we're dealing */ p = proc_create("servers", S_IFREG|S_IRUGO, proc_fs_nfs, &nfs_server_list_fops); diff --git a/fs/proc/inode.c b/fs/proc/inode.c index e11dc22c6511..d78ade305541 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -58,11 +58,8 @@ static void proc_delete_inode(struct inode *inode) /* Let go of any associated proc directory entry */ de = PROC_I(inode)->pde; - if (de) { - if (de->owner) - module_put(de->owner); + if (de) de_put(de); - } if (PROC_I(inode)->sysctl) sysctl_head_put(PROC_I(inode)->sysctl); clear_inode(inode); @@ -449,12 +446,9 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, { struct inode * inode; - if (!try_module_get(de->owner)) - goto out_mod; - inode = iget_locked(sb, ino); if (!inode) - goto out_ino; + return NULL; if (inode->i_state & I_NEW) { inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; PROC_I(inode)->fd = 0; @@ -485,16 +479,9 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, } } unlock_new_inode(inode); - } else { - module_put(de->owner); + } else de_put(de); - } return inode; - -out_ino: - module_put(de->owner); -out_mod: - return NULL; } int proc_fill_super(struct super_block *s) diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index d153946d6d15..4a9e0f65ae60 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -152,7 +152,6 @@ void proc_tty_register_driver(struct tty_driver *driver) if (!ent) return; ent->read_proc = driver->ops->read_proc; - ent->owner = driver->owner; ent->data = driver; driver->proc_entry = ent; diff --git a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c index d5066400638a..9229e5514a4e 100644 --- a/fs/reiserfs/procfs.c +++ b/fs/reiserfs/procfs.c @@ -492,7 +492,6 @@ int reiserfs_proc_info_init(struct super_block *sb) spin_lock_init(&__PINFO(sb).lock); REISERFS_SB(sb)->procdir = proc_mkdir(b, proc_info_root); if (REISERFS_SB(sb)->procdir) { - REISERFS_SB(sb)->procdir->owner = THIS_MODULE; REISERFS_SB(sb)->procdir->data = sb; add_file(sb, "version", show_version); add_file(sb, "super", show_super); @@ -556,9 +555,7 @@ int reiserfs_proc_info_global_init(void) { if (proc_info_root == NULL) { proc_info_root = proc_mkdir(proc_info_root_name, NULL); - if (proc_info_root) { - proc_info_root->owner = THIS_MODULE; - } else { + if (!proc_info_root) { reiserfs_warning(NULL, "cannot create /proc/%s", proc_info_root_name); return 1; diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index 62b73668b602..f7c9c75a2775 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -230,6 +230,6 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg) automatically be dstroyed when the interface is destroyed. */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, read_proc_t *read_proc, - void *data, struct module *owner); + void *data); #endif /* __LINUX_IPMI_SMI_H */ diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index b8bdb96eff78..fbfa3d44d33d 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -41,9 +41,6 @@ enum { * while parent/subdir create the directory structure (every * /proc file has a parent, but "subdir" is NULL for all * non-directory entries). - * - * "owner" is used to protect module - * from unloading while proc_dir_entry is in use */ typedef int (read_proc_t)(char *page, char **start, off_t off, @@ -70,7 +67,6 @@ struct proc_dir_entry { * somewhere. */ const struct file_operations *proc_fops; - struct module *owner; struct proc_dir_entry *next, *parent, *subdir; void *data; read_proc_t *read_proc; diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c index 162199a2d74f..fd8e0847b254 100644 --- a/net/appletalk/atalk_proc.c +++ b/net/appletalk/atalk_proc.c @@ -281,7 +281,6 @@ int __init atalk_proc_init(void) atalk_proc_dir = proc_mkdir("atalk", init_net.proc_net); if (!atalk_proc_dir) goto out; - atalk_proc_dir->owner = THIS_MODULE; p = proc_create("interface", S_IRUGO, atalk_proc_dir, &atalk_seq_interface_fops); diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c index 4990541ef5da..1a0f5ccea9c4 100644 --- a/net/atm/mpoa_proc.c +++ b/net/atm/mpoa_proc.c @@ -281,7 +281,6 @@ int mpc_proc_init(void) printk(KERN_ERR "Unable to initialize /proc/atm/%s\n", STAT_FILE_NAME); return -ENOMEM; } - p->owner = THIS_MODULE; return 0; } diff --git a/net/atm/proc.c b/net/atm/proc.c index 49487b313f22..e7b3b273907d 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -476,7 +476,6 @@ int __init atm_proc_init(void) atm_proc_root, e->proc_fops); if (!dirent) goto err_out_remove; - dirent->owner = THIS_MODULE; e->dirent = dirent; } ret = 0; diff --git a/net/can/bcm.c b/net/can/bcm.c index b7c7d4651136..95d7f32643ae 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1604,10 +1604,6 @@ static int __init bcm_module_init(void) /* create /proc/net/can-bcm directory */ proc_dir = proc_mkdir("can-bcm", init_net.proc_net); - - if (proc_dir) - proc_dir->owner = THIS_MODULE; - return 0; } diff --git a/net/can/proc.c b/net/can/proc.c index 520fef5e5398..1463653dbe34 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -473,8 +473,6 @@ void can_init_proc(void) return; } - can_dir->owner = THIS_MODULE; - /* own procfs entries from the AF_CAN core */ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644, can_proc_read_version, NULL); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 32d419f5ac98..3779c1438c11 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3806,7 +3806,6 @@ static int __init pg_init(void) pg_proc_dir = proc_mkdir(PG_PROC_DIR, init_net.proc_net); if (!pg_proc_dir) return -ENODEV; - pg_proc_dir->owner = THIS_MODULE; pe = proc_create(PGCTRL, 0600, pg_proc_dir, &pktgen_fops); if (pe == NULL) { diff --git a/net/irda/irproc.c b/net/irda/irproc.c index 88e80a312732..8ff1861649e8 100644 --- a/net/irda/irproc.c +++ b/net/irda/irproc.c @@ -70,7 +70,6 @@ void __init irda_proc_register(void) proc_irda = proc_mkdir("irda", init_net.proc_net); if (proc_irda == NULL) return; - proc_irda->owner = THIS_MODULE; for (i = 0; i < ARRAY_SIZE(irda_dirs); i++) d = proc_create(irda_dirs[i].name, 0, proc_irda, diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index b58bd7c6cdf8..d208b3396d94 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -236,7 +236,6 @@ int __init llc_proc_init(void) llc_proc_dir = proc_mkdir("llc", init_net.proc_net); if (!llc_proc_dir) goto out; - llc_proc_dir->owner = THIS_MODULE; p = proc_create("socket", S_IRUGO, llc_proc_dir, &llc_seq_socket_fops); if (!p) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index cb198af8887c..8eb3e61cb701 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -106,12 +106,8 @@ static __init int sctp_proc_init(void) goto out_nomem; #ifdef CONFIG_PROC_FS if (!proc_net_sctp) { - struct proc_dir_entry *ent; - ent = proc_mkdir("sctp", init_net.proc_net); - if (ent) { - ent->owner = THIS_MODULE; - proc_net_sctp = ent; - } else + proc_net_sctp = proc_mkdir("sctp", init_net.proc_net); + if (!proc_net_sctp) goto out_free_percpu; } diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 4735caad26ed..20029a79a5de 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -313,7 +313,6 @@ static int create_cache_proc_entries(struct cache_detail *cd) cd->proc_ent = proc_mkdir(cd->name, proc_net_rpc); if (cd->proc_ent == NULL) goto out_nomem; - cd->proc_ent->owner = cd->owner; cd->channel_ent = cd->content_ent = NULL; p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR, @@ -321,7 +320,6 @@ static int create_cache_proc_entries(struct cache_detail *cd) cd->flush_ent = p; if (p == NULL) goto out_nomem; - p->owner = cd->owner; if (cd->cache_request || cd->cache_parse) { p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR, @@ -329,7 +327,6 @@ static int create_cache_proc_entries(struct cache_detail *cd) cd->channel_ent = p; if (p == NULL) goto out_nomem; - p->owner = cd->owner; } if (cd->cache_show) { p = proc_create_data("content", S_IFREG|S_IRUSR|S_IWUSR, @@ -337,7 +334,6 @@ static int create_cache_proc_entries(struct cache_detail *cd) cd->content_ent = p; if (p == NULL) goto out_nomem; - p->owner = cd->owner; } return 0; out_nomem: diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 085372ef4feb..1ef6e46d9da2 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -262,14 +262,8 @@ void rpc_proc_init(void) { dprintk("RPC: registering /proc/net/rpc\n"); - if (!proc_net_rpc) { - struct proc_dir_entry *ent; - ent = proc_mkdir("rpc", init_net.proc_net); - if (ent) { - ent->owner = THIS_MODULE; - proc_net_rpc = ent; - } - } + if (!proc_net_rpc) + proc_net_rpc = proc_mkdir("rpc", init_net.proc_net); } void diff --git a/sound/core/info.c b/sound/core/info.c index 70fa87189f36..35df614f6c55 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -154,11 +154,6 @@ EXPORT_SYMBOL(snd_seq_root); struct snd_info_entry *snd_oss_root; #endif -static inline void snd_info_entry_prepare(struct proc_dir_entry *de) -{ - de->owner = THIS_MODULE; -} - static void snd_remove_proc_entry(struct proc_dir_entry *parent, struct proc_dir_entry *de) { @@ -522,32 +517,11 @@ static const struct file_operations snd_info_entry_operations = .release = snd_info_entry_release, }; -/** - * snd_create_proc_entry - create a procfs entry - * @name: the name of the proc file - * @mode: the file permission bits, S_Ixxx - * @parent: the parent proc-directory entry - * - * Creates a new proc file entry with the given name and permission - * on the given directory. - * - * Returns the pointer of new instance or NULL on failure. - */ -static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, - struct proc_dir_entry *parent) -{ - struct proc_dir_entry *p; - p = create_proc_entry(name, mode, parent); - if (p) - snd_info_entry_prepare(p); - return p; -} - int __init snd_info_init(void) { struct proc_dir_entry *p; - p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, NULL); + p = create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, NULL); if (p == NULL) return -ENOMEM; snd_proc_root = p; @@ -974,12 +948,11 @@ int snd_info_register(struct snd_info_entry * entry) return -ENXIO; root = entry->parent == NULL ? snd_proc_root : entry->parent->p; mutex_lock(&info_mutex); - p = snd_create_proc_entry(entry->name, entry->mode, root); + p = create_proc_entry(entry->name, entry->mode, root); if (!p) { mutex_unlock(&info_mutex); return -ENOMEM; } - p->owner = entry->module; if (!S_ISDIR(entry->mode)) p->proc_fops = &snd_info_entry_operations; p->size = entry->size; -- cgit v1.2.3-59-g8ed1b From 11373542344bdc35be1e6e68b0baadd1b6f7acbb Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 31 Mar 2009 15:18:37 +1100 Subject: Documentation/md.txt update Update md.txt to reflect recent changes in a number of sysfs attributes. Signed-off-by: NeilBrown --- Documentation/md.txt | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/md.txt b/Documentation/md.txt index 1da9d1b1793f..4edd39ec7db9 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -164,15 +164,19 @@ All md devices contain: raid_disks a text file with a simple number indicating the number of devices in a fully functional array. If this is not yet known, the file - will be empty. If an array is being resized (not currently - possible) this will contain the larger of the old and new sizes. - Some raid level (RAID1) allow this value to be set while the - array is active. This will reconfigure the array. Otherwise - it can only be set while assembling an array. + will be empty. If an array is being resized this will contain + the new number of devices. + Some raid levels allow this value to be set while the array is + active. This will reconfigure the array. Otherwise it can only + be set while assembling an array. + A change to this attribute will not be permitted if it would + reduce the size of the array. To reduce the number of drives + in an e.g. raid5, the array size must first be reduced by + setting the 'array_size' attribute. chunk_size - This is the size if bytes for 'chunks' and is only relevant to - raid levels that involve striping (1,4,5,6,10). The address space + This is the size in bytes for 'chunks' and is only relevant to + raid levels that involve striping (0,4,5,6,10). The address space of the array is conceptually divided into chunks and consecutive chunks are striped onto neighbouring devices. The size should be at least PAGE_SIZE (4k) and should be a power @@ -183,6 +187,20 @@ All md devices contain: simply a number that is interpretted differently by different levels. It can be written while assembling an array. + array_size + This can be used to artificially constrain the available space in + the array to be less than is actually available on the combined + devices. Writing a number (in Kilobytes) which is less than + the available size will set the size. Any reconfiguration of the + array (e.g. adding devices) will not cause the size to change. + Writing the word 'default' will cause the effective size of the + array to be whatever size is actually available based on + 'level', 'chunk_size' and 'component_size'. + + This can be used to reduce the size of the array before reducing + the number of devices in a raid4/5/6, or to support external + metadata formats which mandate such clipping. + reshape_position This is either "none" or a sector number within the devices of the array where "reshape" is up to. If this is set, the three @@ -207,6 +225,11 @@ All md devices contain: about the array. It can be 0.90 (traditional format), 1.0, 1.1, 1.2 (newer format in varying locations) or "none" indicating that the kernel isn't managing metadata at all. + Alternately it can be "external:" followed by a string which + is set by user-space. This indicates that metadata is managed + by a user-space program. Any device failure or other event that + requires a metadata update will cause array activity to be + suspended until the event is acknowledged. resync_start The point at which resync should start. If no resync is needed, -- cgit v1.2.3-59-g8ed1b From 853116a10544206b6b2cf42ebc9d78fba2668888 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 14 Jan 2009 23:03:17 -0800 Subject: regulator: add get_status() Based on previous LKML discussions: * Update docs for regulator sysfs class attributes to highlight the fact that all current attributes are intended to be control inputs, including notably "state" and "opmode" which previously implied otherwise. * Define a new regulator driver get_status() method, which is the first method reporting regulator outputs instead of inputs. It can report on/off and error status; or instead of simply "on", report the actual operating mode. For the moment, this is a sysfs-only interface, not accessible to regulator clients. Such clients can use the current notification interfaces to detect errors, if the regulator reports them. Signed-off-by: David Brownell Signed-off-by: Liam Girdwood --- Documentation/ABI/testing/sysfs-class-regulator | 57 +++++++++++++++++++++---- drivers/regulator/core.c | 46 ++++++++++++++++++++ include/linux/regulator/driver.h | 17 ++++++++ 3 files changed, 111 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator index 873ef1fc1569..e091fa873792 100644 --- a/Documentation/ABI/testing/sysfs-class-regulator +++ b/Documentation/ABI/testing/sysfs-class-regulator @@ -4,8 +4,8 @@ KernelVersion: 2.6.26 Contact: Liam Girdwood Description: Some regulator directories will contain a field called - state. This reports the regulator enable status, for - regulators which can report that value. + state. This reports the regulator enable control, for + regulators which can report that input value. This will be one of the following strings: @@ -14,16 +14,54 @@ Description: 'unknown' 'enabled' means the regulator output is ON and is supplying - power to the system. + power to the system (assuming no error prevents it). 'disabled' means the regulator output is OFF and is not - supplying power to the system.. + supplying power to the system (unless some non-Linux + control has enabled it). 'unknown' means software cannot determine the state, or the reported state is invalid. NOTE: this field can be used in conjunction with microvolts - and microamps to determine regulator output levels. + or microamps to determine configured regulator output levels. + + +What: /sys/class/regulator/.../status +Description: + Some regulator directories will contain a field called + "status". This reports the current regulator status, for + regulators which can report that output value. + + This will be one of the following strings: + + off + on + error + fast + normal + idle + standby + + "off" means the regulator is not supplying power to the + system. + + "on" means the regulator is supplying power to the system, + and the regulator can't report a detailed operation mode. + + "error" indicates an out-of-regulation status such as being + disabled due to thermal shutdown, or voltage being unstable + because of problems with the input power supply. + + "fast", "normal", "idle", and "standby" are all detailed + regulator operation modes (described elsewhere). They + imply "on", but provide more detail. + + Note that regulator status is a function of many inputs, + not limited to control inputs from Linux. For example, + the actual load presented may trigger "error" status; or + a regulator may be enabled by another user, even though + Linux did not enable it. What: /sys/class/regulator/.../type @@ -58,7 +96,7 @@ Description: Some regulator directories will contain a field called microvolts. This holds the regulator output voltage setting measured in microvolts (i.e. E-6 Volts), for regulators - which can report that voltage. + which can report the control input for voltage. NOTE: This value should not be used to determine the regulator output voltage level as this value is the same regardless of @@ -73,7 +111,7 @@ Description: Some regulator directories will contain a field called microamps. This holds the regulator output current limit setting measured in microamps (i.e. E-6 Amps), for regulators - which can report that current. + which can report the control input for a current limit. NOTE: This value should not be used to determine the regulator output current level as this value is the same regardless of @@ -87,7 +125,7 @@ Contact: Liam Girdwood Description: Some regulator directories will contain a field called opmode. This holds the current regulator operating mode, - for regulators which can report it. + for regulators which can report that control input value. The opmode value can be one of the following strings: @@ -101,7 +139,8 @@ Description: NOTE: This value should not be used to determine the regulator output operating mode as this value is the same regardless of - whether the regulator is enabled or disabled. + whether the regulator is enabled or disabled. A "status" + attribute may be available to determine the actual mode. What: /sys/class/regulator/.../min_microvolts diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f511a406fcaa..0ff95c3ccf5b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -312,6 +312,47 @@ static ssize_t regulator_state_show(struct device *dev, } static DEVICE_ATTR(state, 0444, regulator_state_show, NULL); +static ssize_t regulator_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + int status; + char *label; + + status = rdev->desc->ops->get_status(rdev); + if (status < 0) + return status; + + switch (status) { + case REGULATOR_STATUS_OFF: + label = "off"; + break; + case REGULATOR_STATUS_ON: + label = "on"; + break; + case REGULATOR_STATUS_ERROR: + label = "error"; + break; + case REGULATOR_STATUS_FAST: + label = "fast"; + break; + case REGULATOR_STATUS_NORMAL: + label = "normal"; + break; + case REGULATOR_STATUS_IDLE: + label = "idle"; + break; + case REGULATOR_STATUS_STANDBY: + label = "standby"; + break; + default: + return -ERANGE; + } + + return sprintf(buf, "%s\n", label); +} +static DEVICE_ATTR(status, 0444, regulator_status_show, NULL); + static ssize_t regulator_min_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1744,6 +1785,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev) if (status < 0) return status; } + if (ops->get_status) { + status = device_create_file(dev, &dev_attr_status); + if (status < 0) + return status; + } /* some attributes are type-specific */ if (rdev->desc->type == REGULATOR_CURRENT) { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 2dae05705f13..6e957aae7629 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -21,6 +21,17 @@ struct regulator_dev; struct regulator_init_data; +enum regulator_status { + REGULATOR_STATUS_OFF, + REGULATOR_STATUS_ON, + REGULATOR_STATUS_ERROR, + /* fast/normal/idle/standby are flavors of "on" */ + REGULATOR_STATUS_FAST, + REGULATOR_STATUS_NORMAL, + REGULATOR_STATUS_IDLE, + REGULATOR_STATUS_STANDBY, +}; + /** * struct regulator_ops - regulator operations. * @@ -72,6 +83,12 @@ struct regulator_ops { int (*set_mode) (struct regulator_dev *, unsigned int mode); unsigned int (*get_mode) (struct regulator_dev *); + /* report regulator status ... most other accessors report + * control inputs, this reports results of combining inputs + * from Linux (and other sources) with the actual load. + */ + int (*get_status)(struct regulator_dev *); + /* get most efficient regulator operating mode for load */ unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV, int output_uV, int load_uA); -- cgit v1.2.3-59-g8ed1b From 214c8adb87b880690b103e86baffc46f8288fa62 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Tue, 28 Oct 2008 17:22:01 +0200 Subject: exofs: Documentation Added some documentation in exofs.txt, as well as a BUGS file. For further reading, operation instructions, example scripts and up to date infomation and code please see: http://open-osd.org Signed-off-by: Boaz Harrosh --- Documentation/filesystems/exofs.txt | 176 ++++++++++++++++++++++++++++++++++++ fs/exofs/BUGS | 3 + 2 files changed, 179 insertions(+) create mode 100644 Documentation/filesystems/exofs.txt create mode 100644 fs/exofs/BUGS (limited to 'Documentation') diff --git a/Documentation/filesystems/exofs.txt b/Documentation/filesystems/exofs.txt new file mode 100644 index 000000000000..0ced74c2f73c --- /dev/null +++ b/Documentation/filesystems/exofs.txt @@ -0,0 +1,176 @@ +=============================================================================== +WHAT IS EXOFS? +=============================================================================== + +exofs is a file system that uses an OSD and exports the API of a normal Linux +file system. Users access exofs like any other local file system, and exofs +will in turn issue commands to the local OSD initiator. + +OSD is a new T10 command set that views storage devices not as a large/flat +array of sectors but as a container of objects, each having a length, quota, +time attributes and more. Each object is addressed by a 64bit ID, and is +contained in a 64bit ID partition. Each object has associated attributes +attached to it, which are integral part of the object and provide metadata about +the object. The standard defines some common obligatory attributes, but user +attributes can be added as needed. + +=============================================================================== +ENVIRONMENT +=============================================================================== + +To use this file system, you need to have an object store to run it on. You +may download a target from: +http://open-osd.org + +See Documentation/scsi/osd.txt for how to setup a working osd environment. + +=============================================================================== +USAGE +=============================================================================== + +1. Download and compile exofs and open-osd initiator: + You need an external Kernel source tree or kernel headers from your + distribution. (anything based on 2.6.26 or later). + + a. download open-osd including exofs source using: + [parent-directory]$ git clone git://git.open-osd.org/open-osd.git + + b. Build the library module like this: + [parent-directory]$ make -C KSRC=$(KER_DIR) open-osd + + This will build both the open-osd initiator as well as the exofs kernel + module. Use whatever parameters you compiled your Kernel with and + $(KER_DIR) above pointing to the Kernel you compile against. See the file + open-osd/top-level-Makefile for an example. + +2. Get the OSD initiator and target set up properly, and login to the target. + See Documentation/scsi/osd.txt for farther instructions. Also see ./do-osd + for example script that does all these steps. + +3. Insmod the exofs.ko module: + [exofs]$ insmod exofs.ko + +4. Make sure the directory where you want to mount exists. If not, create it. + (For example, mkdir /mnt/exofs) + +5. At first run you will need to invoke the mkfs.exofs application + + As an example, this will create the file system on: + /dev/osd0 partition ID 65536 + + mkfs.exofs --pid=65536 --format /dev/osd0 + + The --format is optional if not specified no OSD_FORMAT will be + preformed and a clean file system will be created in the specified pid, + in the available space of the target. (Use --format=size_in_meg to limit + the total LUN space available) + + If pid already exist it will be deleted and a new one will be created in it's + place. Be careful. + + An exofs lives inside a single OSD partition. You can create multiple exofs + filesystems on the same device using multiple pids. + + (run mkfs.exofs without any parameters for usage help message) + +6. Mount the file system. + + For example, to mount /dev/osd0, partition ID 0x10000 on /mnt/exofs: + + mount -t exofs -o pid=65536 /dev/osd0 /mnt/exofs/ + +7. For reference (See do-exofs example script): + do-exofs start - an example of how to perform the above steps. + do-exofs stop - an example of how to unmount the file system. + do-exofs format - an example of how to format and mkfs a new exofs. + +8. Extra compilation flags (uncomment in fs/exofs/Kbuild): + CONFIG_EXOFS_DEBUG - for debug messages and extra checks. + +=============================================================================== +exofs mount options +=============================================================================== +Similar to any mount command: + mount -t exofs -o exofs_options /dev/osdX mount_exofs_directory + +Where: + -t exofs: specifies the exofs file system + + /dev/osdX: X is a decimal number. /dev/osdX was created after a successful + login into an OSD target. + + mount_exofs_directory: The directory to mount the file system on + + exofs specific options: Options are separated by commas (,) + pid= - The partition number to mount/create as + container of the filesystem. + This option is mandatory + to= - Timeout in ticks for a single command + default is (60 * HZ) [for debugging only] + +=============================================================================== +DESIGN +=============================================================================== + +* The file system control block (AKA on-disk superblock) resides in an object + with a special ID (defined in common.h). + Information included in the file system control block is used to fill the + in-memory superblock structure at mount time. This object is created before + the file system is used by mkexofs.c It contains information such as: + - The file system's magic number + - The next inode number to be allocated + +* Each file resides in its own object and contains the data (and it will be + possible to extend the file over multiple objects, though this has not been + implemented yet). + +* A directory is treated as a file, and essentially contains a list of pairs for files that are found in that directory. The object + IDs correspond to the files' inode numbers and will be allocated according to + a bitmap (stored in a separate object). Now they are allocated using a + counter. + +* Each file's control block (AKA on-disk inode) is stored in its object's + attributes. This applies to both regular files and other types (directories, + device files, symlinks, etc.). + +* Credentials are generated per object (inode and superblock) when they is + created in memory (read off disk or created). The credential works for all + operations and is used as long as the object remains in memory. + +* Async OSD operations are used whenever possible, but the target may execute + them out of order. The operations that concern us are create, delete, + readpage, writepage, update_inode, and truncate. The following pairs of + operations should execute in the order written, and we need to prevent them + from executing in reverse order: + - The following are handled with the OBJ_CREATED and OBJ_2BCREATED + flags. OBJ_CREATED is set when we know the object exists on the OSD - + in create's callback function, and when we successfully do a read_inode. + OBJ_2BCREATED is set in the beginning of the create function, so we + know that we should wait. + - create/delete: delete should wait until the object is created + on the OSD. + - create/readpage: readpage should be able to return a page + full of zeroes in this case. If there was a write already + en-route (i.e. create, writepage, readpage) then the page + would be locked, and so it would really be the same as + create/writepage. + - create/writepage: if writepage is called for a sync write, it + should wait until the object is created on the OSD. + Otherwise, it should just return. + - create/truncate: truncate should wait until the object is + created on the OSD. + - create/update_inode: update_inode should wait until the + object is created on the OSD. + - Handled by VFS locks: + - readpage/delete: shouldn't happen because of page lock. + - writepage/delete: shouldn't happen because of page lock. + - readpage/writepage: shouldn't happen because of page lock. + +=============================================================================== +LICENSE/COPYRIGHT +=============================================================================== +The exofs file system is based on ext2 v0.5b (distributed with the Linux kernel +version 2.6.10). All files include the original copyrights, and the license +is GPL version 2 (only version 2, as is true for the Linux kernel). The +Linux kernel can be downloaded from www.kernel.org. diff --git a/fs/exofs/BUGS b/fs/exofs/BUGS new file mode 100644 index 000000000000..1b2d4c63a579 --- /dev/null +++ b/fs/exofs/BUGS @@ -0,0 +1,3 @@ +- Out-of-space may cause a severe problem if the object (and directory entry) + were written, but the inode attributes failed. Then if the filesystem was + unmounted and mounted the kernel can get into an endless loop doing a readdir. -- cgit v1.2.3-59-g8ed1b From 2584e517320bd48dc8d20e38a2621a2dbe58fade Mon Sep 17 00:00:00 2001 From: Ravikiran G Thirumalai Date: Tue, 31 Mar 2009 15:21:26 -0700 Subject: mm: reintroduce and deprecate rlimit based access for SHM_HUGETLB Allow non root users with sufficient mlock rlimits to be able to allocate hugetlb backed shm for now. Deprecate this though. This is being deprecated because the mlock based rlimit checks for SHM_HUGETLB is not consistent with mmap based huge page allocations. Signed-off-by: Ravikiran Thirumalai Reviewed-by: Mel Gorman Cc: William Lee Irwin III Cc: Adam Litke Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/feature-removal-schedule.txt | 12 ++++++++++++ fs/hugetlbfs/inode.c | 13 +++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 5e02b83ac12b..ea7d1bdad34d 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -311,6 +311,18 @@ Who: Vlad Yasevich --------------------------- +What: Ability for non root users to shm_get hugetlb pages based on mlock + resource limits +When: 2.6.31 +Why: Non root users need to be part of /proc/sys/vm/hugetlb_shm_group or + have CAP_IPC_LOCK to be able to allocate shm segments backed by + huge pages. The mlock based rlimit check to allow shm hugetlb is + inconsistent with mmap based allocations. Hence it is being + deprecated. +Who: Ravikiran Thirumalai + +--------------------------- + What: CONFIG_THERMAL_HWMON When: January 2009 Why: This option was introduced just to allow older lm-sensors userspace diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index bc56df8ce001..23a3c76711e0 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -949,6 +949,7 @@ static int can_do_hugetlb_shm(void) struct file *hugetlb_file_setup(const char *name, size_t size, int acctflag) { int error = -ENOMEM; + int unlock_shm = 0; struct file *file; struct inode *inode; struct dentry *dentry, *root; @@ -958,8 +959,14 @@ struct file *hugetlb_file_setup(const char *name, size_t size, int acctflag) if (!hugetlbfs_vfsmount) return ERR_PTR(-ENOENT); - if (!can_do_hugetlb_shm()) - return ERR_PTR(-EPERM); + if (!can_do_hugetlb_shm()) { + if (user_shm_lock(size, user)) { + unlock_shm = 1; + WARN_ONCE(1, + "Using mlock ulimits for SHM_HUGETLB deprecated\n"); + } else + return ERR_PTR(-EPERM); + } root = hugetlbfs_vfsmount->mnt_root; quick_string.name = name; @@ -999,6 +1006,8 @@ out_inode: out_dentry: dput(dentry); out_shm_unlock: + if (unlock_shm) + user_shm_unlock(size, user); return ERR_PTR(error); } -- cgit v1.2.3-59-g8ed1b From c2ec175c39f62949438354f603f4aa170846aabb Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Tue, 31 Mar 2009 15:23:21 -0700 Subject: mm: page_mkwrite change prototype to match fault Change the page_mkwrite prototype to take a struct vm_fault, and return VM_FAULT_xxx flags. There should be no functional change. This makes it possible to return much more detailed error information to the VM (and also can provide more information eg. virtual_address to the driver, which might be important in some special cases). This is required for a subsequent fix. And will also make it easier to merge page_mkwrite() with fault() in future. Signed-off-by: Nick Piggin Cc: Chris Mason Cc: Trond Myklebust Cc: Miklos Szeredi Cc: Steven Whitehouse Cc: Mark Fasheh Cc: Joel Becker Cc: Artem Bityutskiy Cc: Felix Blyakher Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 2 +- drivers/video/fb_defio.c | 3 ++- fs/btrfs/ctree.h | 2 +- fs/btrfs/inode.c | 5 ++++- fs/buffer.c | 6 +++++- fs/ext4/ext4.h | 2 +- fs/ext4/inode.c | 5 ++++- fs/fuse/file.c | 3 ++- fs/gfs2/ops_file.c | 5 ++++- fs/nfs/file.c | 5 ++++- fs/ocfs2/mmap.c | 6 ++++-- fs/ubifs/file.c | 9 ++++++--- fs/xfs/linux-2.6/xfs_file.c | 4 ++-- include/linux/buffer_head.h | 2 +- include/linux/mm.h | 3 ++- mm/memory.c | 26 ++++++++++++++++++++++---- 16 files changed, 65 insertions(+), 23 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 4e78ce677843..76efe5b71d7d 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -505,7 +505,7 @@ prototypes: void (*open)(struct vm_area_struct*); void (*close)(struct vm_area_struct*); int (*fault)(struct vm_area_struct*, struct vm_fault *); - int (*page_mkwrite)(struct vm_area_struct *, struct page *); + int (*page_mkwrite)(struct vm_area_struct *, struct vm_fault *); int (*access)(struct vm_area_struct *, unsigned long, void*, int, int); locking rules: diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 082026546aee..0a7a6679ee6e 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -85,8 +85,9 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); /* vm_ops->page_mkwrite handler */ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, - struct page *page) + struct vm_fault *vmf) { + struct page *page = vmf->page; struct fb_info *info = vma->vm_private_data; struct fb_deferred_io *fbdefio = info->fbdefio; struct page *cur; diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 5e1d4e30e9d8..7dd1b6d0bf32 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2060,7 +2060,7 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset, unsigned long btrfs_force_ra(struct address_space *mapping, struct file_ra_state *ra, struct file *file, pgoff_t offset, pgoff_t last_index); -int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page); +int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); int btrfs_readpage(struct file *file, struct page *page); void btrfs_delete_inode(struct inode *inode); void btrfs_put_inode(struct inode *inode); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7d4f948bc22a..ec5423790bbb 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4292,8 +4292,9 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset) * beyond EOF, then the page is guaranteed safe against truncation until we * unlock the page. */ -int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page) +int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct inode *inode = fdentry(vma->vm_file)->d_inode; struct btrfs_root *root = BTRFS_I(inode)->root; struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; @@ -4362,6 +4363,8 @@ again: out_unlock: unlock_page(page); out: + if (ret) + ret = VM_FAULT_SIGBUS; return ret; } diff --git a/fs/buffer.c b/fs/buffer.c index 73abe6d8218c..6d51a3da362c 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2313,9 +2313,10 @@ int block_commit_write(struct page *page, unsigned from, unsigned to) * unlock the page. */ int -block_page_mkwrite(struct vm_area_struct *vma, struct page *page, +block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, get_block_t get_block) { + struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; unsigned long end; loff_t size; @@ -2340,6 +2341,9 @@ block_page_mkwrite(struct vm_area_struct *vma, struct page *page, ret = block_commit_write(page, 0, end); out_unlock: + if (ret) + ret = VM_FAULT_SIGBUS; + unlock_page(page); return ret; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 6083bb38057b..990c94000924 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1098,7 +1098,7 @@ extern int ext4_meta_trans_blocks(struct inode *, int nrblocks, int idxblocks); extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks); extern int ext4_block_truncate_page(handle_t *handle, struct address_space *mapping, loff_t from); -extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page); +extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); extern qsize_t ext4_get_reserved_space(struct inode *inode); /* ioctl.c */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 71d3ecd5db79..dd82ff390067 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5146,8 +5146,9 @@ static int ext4_bh_unmapped(handle_t *handle, struct buffer_head *bh) return !buffer_mapped(bh); } -int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page) +int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; loff_t size; unsigned long len; int ret = -EINVAL; @@ -5199,6 +5200,8 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page) goto out_unlock; ret = 0; out_unlock: + if (ret) + ret = VM_FAULT_SIGBUS; up_read(&inode->i_alloc_sem); return ret; } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 821d10f719bd..4e340fedf768 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1234,8 +1234,9 @@ static void fuse_vma_close(struct vm_area_struct *vma) * - sync(2) * - try_to_free_pages() with order > PAGE_ALLOC_COSTLY_ORDER */ -static int fuse_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int fuse_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; /* * Don't use page->mapping as it may become NULL from a * concurrent truncate. diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index 3b9e8de3500b..70b9b8548945 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -337,8 +337,9 @@ static int gfs2_allocate_page_backing(struct page *page) * blocks allocated on disk to back that page. */ -static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); @@ -412,6 +413,8 @@ out_unlock: gfs2_glock_dq(&gh); out: gfs2_holder_uninit(&gh); + if (ret) + ret = VM_FAULT_SIGBUS; return ret; } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 90f292b520d2..cec79392e4ba 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -451,8 +451,9 @@ const struct address_space_operations nfs_file_aops = { .launder_page = nfs_launder_page, }; -static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct file *filp = vma->vm_file; struct dentry *dentry = filp->f_path.dentry; unsigned pagelen; @@ -483,6 +484,8 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) ret = pagelen; out_unlock: unlock_page(page); + if (ret) + ret = VM_FAULT_SIGBUS; return ret; } diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index eea1d24713ea..b606496b72ec 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -154,8 +154,9 @@ out: return ret; } -static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; struct buffer_head *di_bh = NULL; sigset_t blocked, oldset; @@ -196,7 +197,8 @@ out: ret2 = ocfs2_vm_op_unblock_sigs(&oldset); if (ret2 < 0) mlog_errno(ret2); - + if (ret) + ret = VM_FAULT_SIGBUS; return ret; } diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 93b6de51f261..0ff89fe71e51 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1434,8 +1434,9 @@ static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags) * mmap()d file has taken write protection fault and is being made * writable. UBIFS must ensure page is budgeted for. */ -static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) +static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { + struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; struct ubifs_info *c = inode->i_sb->s_fs_info; struct timespec now = ubifs_current_time(inode); @@ -1447,7 +1448,7 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) ubifs_assert(!(inode->i_sb->s_flags & MS_RDONLY)); if (unlikely(c->ro_media)) - return -EROFS; + return VM_FAULT_SIGBUS; /* -EROFS */ /* * We have not locked @page so far so we may budget for changing the @@ -1480,7 +1481,7 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) if (err == -ENOSPC) ubifs_warn("out of space for mmapped file " "(inode number %lu)", inode->i_ino); - return err; + return VM_FAULT_SIGBUS; } lock_page(page); @@ -1520,6 +1521,8 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) out_unlock: unlock_page(page); ubifs_release_budget(c, &req); + if (err) + err = VM_FAULT_SIGBUS; return err; } diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index e14c4e3aea0c..f4e255441574 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -234,9 +234,9 @@ xfs_file_mmap( STATIC int xfs_vm_page_mkwrite( struct vm_area_struct *vma, - struct page *page) + struct vm_fault *vmf) { - return block_page_mkwrite(vma, page, xfs_get_blocks); + return block_page_mkwrite(vma, vmf, xfs_get_blocks); } const struct file_operations xfs_file_operations = { diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index f19fd9045ea0..3d7bcde2e332 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -216,7 +216,7 @@ int cont_write_begin(struct file *, struct address_space *, loff_t, get_block_t *, loff_t *); int generic_cont_expand_simple(struct inode *inode, loff_t size); int block_commit_write(struct page *page, unsigned from, unsigned to); -int block_page_mkwrite(struct vm_area_struct *vma, struct page *page, +int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, get_block_t get_block); void block_sync_page(struct page *); sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); diff --git a/include/linux/mm.h b/include/linux/mm.h index 2223f8dfa568..aeabe953ba4f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -135,6 +135,7 @@ extern pgprot_t protection_map[16]; #define FAULT_FLAG_WRITE 0x01 /* Fault was a write access */ #define FAULT_FLAG_NONLINEAR 0x02 /* Fault was via a nonlinear mapping */ +#define FAULT_FLAG_MKWRITE 0x04 /* Fault was mkwrite of existing pte */ /* * This interface is used by x86 PAT code to identify a pfn mapping that is @@ -187,7 +188,7 @@ struct vm_operations_struct { /* notification that a previously read-only page is about to become * writable, if an error is returned it will cause a SIGBUS */ - int (*page_mkwrite)(struct vm_area_struct *vma, struct page *page); + int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf); /* called by access_process_vm when get_user_pages() fails, typically * for use by special VMAs that can switch between memory and hardware diff --git a/mm/memory.c b/mm/memory.c index 5b4ad5e4f98d..cf6873e91c6a 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1945,6 +1945,15 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, * get_user_pages(.write=1, .force=1). */ if (vma->vm_ops && vma->vm_ops->page_mkwrite) { + struct vm_fault vmf; + int tmp; + + vmf.virtual_address = (void __user *)(address & + PAGE_MASK); + vmf.pgoff = old_page->index; + vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; + vmf.page = old_page; + /* * Notify the address space that the page is about to * become writable so that it can prohibit this or wait @@ -1956,8 +1965,12 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, page_cache_get(old_page); pte_unmap_unlock(page_table, ptl); - if (vma->vm_ops->page_mkwrite(vma, old_page) < 0) + tmp = vma->vm_ops->page_mkwrite(vma, &vmf); + if (unlikely(tmp & + (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { + ret = tmp; goto unwritable_page; + } /* * Since we dropped the lock we need to revalidate @@ -2106,7 +2119,7 @@ oom: unwritable_page: page_cache_release(old_page); - return VM_FAULT_SIGBUS; + return ret; } /* @@ -2648,9 +2661,14 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, * to become writable */ if (vma->vm_ops->page_mkwrite) { + int tmp; + unlock_page(page); - if (vma->vm_ops->page_mkwrite(vma, page) < 0) { - ret = VM_FAULT_SIGBUS; + vmf.flags |= FAULT_FLAG_MKWRITE; + tmp = vma->vm_ops->page_mkwrite(vma, &vmf); + if (unlikely(tmp & + (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { + ret = tmp; anon = 1; /* no anon but release vmf.page */ goto out_unlocked; } -- cgit v1.2.3-59-g8ed1b From c2d7543851849a6923680cdd7e1047ed1a84a1c5 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 31 Mar 2009 15:23:46 -0700 Subject: filesystem freeze: allow SysRq emergency thaw to thaw frozen filesystems Now that the filesystem freeze operation has been elevated to the VFS, and is just an ioctl away, some sort of safety net for unintentionally frozen root filesystems may be in order. The timeout thaw originally proposed did not get merged, but perhaps something like this would be useful in emergencies. For example, freeze /path/to/mountpoint may freeze your root filesystem if you forgot that you had that unmounted. I chose 'j' as the last remaining character other than 'h' which is sort of reserved for help (because help is generated on any unknown character). I've tested this on a non-root fs with multiple (nested) freezers, as well as on a system rendered unresponsive due to a frozen root fs. [randy.dunlap@oracle.com: emergency thaw only if CONFIG_BLOCK enabled] Signed-off-by: Eric Sandeen Cc: Takashi Sato Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/sysrq.txt | 5 +++++ drivers/char/sysrq.c | 19 ++++++++++++++++++- fs/buffer.c | 33 +++++++++++++++++++++++++++++++++ include/linux/fs.h | 1 + 4 files changed, 57 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index 9e592c718afb..afa2946892da 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -81,6 +81,8 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'i' - Send a SIGKILL to all processes, except for init. +'j' - Forcibly "Just thaw it" - filesystems frozen by the FIFREEZE ioctl. + 'k' - Secure Access Key (SAK) Kills all programs on the current virtual console. NOTE: See important comments below in SAK section. @@ -160,6 +162,9 @@ t'E'rm and k'I'll are useful if you have some sort of runaway process you are unable to kill any other way, especially if it's spawning other processes. +"'J'ust thaw it" is useful if your system becomes unresponsive due to a frozen +(probably root) filesystem via the FIFREEZE ioctl. + * Sometimes SysRq seems to get 'stuck' after using it, what can I do? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ That happens to me, also. I've found that tapping shift, alt, and control diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 33a9351c896d..5afe7316c72e 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -346,6 +346,19 @@ static struct sysrq_key_op sysrq_moom_op = { .enable_mask = SYSRQ_ENABLE_SIGNAL, }; +#ifdef CONFIG_BLOCK +static void sysrq_handle_thaw(int key, struct tty_struct *tty) +{ + emergency_thaw_all(); +} +static struct sysrq_key_op sysrq_thaw_op = { + .handler = sysrq_handle_thaw, + .help_msg = "thaw-filesystems(J)", + .action_msg = "Emergency Thaw of all frozen filesystems", + .enable_mask = SYSRQ_ENABLE_SIGNAL, +}; +#endif + static void sysrq_handle_kill(int key, struct tty_struct *tty) { send_sig_all(SIGKILL); @@ -396,9 +409,13 @@ static struct sysrq_key_op *sysrq_key_table[36] = { &sysrq_moom_op, /* f */ /* g: May be registered by ppc for kgdb */ NULL, /* g */ - NULL, /* h */ + NULL, /* h - reserved for help */ &sysrq_kill_op, /* i */ +#ifdef CONFIG_BLOCK + &sysrq_thaw_op, /* j */ +#else NULL, /* j */ +#endif &sysrq_SAK_op, /* k */ #ifdef CONFIG_SMP &sysrq_showallcpus_op, /* l */ diff --git a/fs/buffer.c b/fs/buffer.c index c77b848c3d43..f5f8b15a6e40 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -547,6 +547,39 @@ repeat: return err; } +void do_thaw_all(unsigned long unused) +{ + struct super_block *sb; + char b[BDEVNAME_SIZE]; + + spin_lock(&sb_lock); +restart: + list_for_each_entry(sb, &super_blocks, s_list) { + sb->s_count++; + spin_unlock(&sb_lock); + down_read(&sb->s_umount); + while (sb->s_bdev && !thaw_bdev(sb->s_bdev, sb)) + printk(KERN_WARNING "Emergency Thaw on %s\n", + bdevname(sb->s_bdev, b)); + up_read(&sb->s_umount); + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto restart; + } + spin_unlock(&sb_lock); + printk(KERN_WARNING "Emergency Thaw complete\n"); +} + +/** + * emergency_thaw_all -- forcibly thaw every frozen filesystem + * + * Used for emergency unfreeze of all filesystems via SysRq + */ +void emergency_thaw_all(void) +{ + pdflush_operation(do_thaw_all, 0); +} + /** * sync_mapping_buffers - write out & wait upon a mapping's "associated" buffers * @mapping: the mapping which wants those buffers written diff --git a/include/linux/fs.h b/include/linux/fs.h index 87e7bfc5ebd7..61211ad823fe 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1878,6 +1878,7 @@ extern struct block_device *open_by_devnum(dev_t, fmode_t); extern void invalidate_bdev(struct block_device *); extern int sync_blockdev(struct block_device *bdev); extern struct super_block *freeze_bdev(struct block_device *); +extern void emergency_thaw_all(void); extern int thaw_bdev(struct block_device *bdev, struct super_block *sb); extern int fsync_bdev(struct block_device *); extern int fsync_super(struct super_block *); -- cgit v1.2.3-59-g8ed1b From 3cdbbeebb77348176bd6a03fd86e11bc281c529e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 31 Mar 2009 15:23:53 -0700 Subject: drivers/misc/isl29003.c: driver for the ISL29003 ambient light sensor Add a driver for Intersil's ISL29003 ambient light sensor device plus some documentation. Inspired by tsl2550.c, a driver for a similar device. It is put in drivers/misc for now until the industrial I/O framework gets merged. Signed-off-by: Daniel Mack Acked-by: Jonathan Cameron Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/misc-devices/isl29003 | 62 +++++ drivers/misc/Kconfig | 10 + drivers/misc/Makefile | 1 + drivers/misc/isl29003.c | 470 ++++++++++++++++++++++++++++++++++++ 4 files changed, 543 insertions(+) create mode 100644 Documentation/misc-devices/isl29003 create mode 100644 drivers/misc/isl29003.c (limited to 'Documentation') diff --git a/Documentation/misc-devices/isl29003 b/Documentation/misc-devices/isl29003 new file mode 100644 index 000000000000..c4ff5f38e010 --- /dev/null +++ b/Documentation/misc-devices/isl29003 @@ -0,0 +1,62 @@ +Kernel driver isl29003 +===================== + +Supported chips: +* Intersil ISL29003 +Prefix: 'isl29003' +Addresses scanned: none +Datasheet: +http://www.intersil.com/data/fn/fn7464.pdf + +Author: Daniel Mack + + +Description +----------- +The ISL29003 is an integrated light sensor with a 16-bit integrating type +ADC, I2C user programmable lux range select for optimized counts/lux, and +I2C multi-function control and monitoring capabilities. The internal ADC +provides 16-bit resolution while rejecting 50Hz and 60Hz flicker caused by +artificial light sources. + +The driver allows to set the lux range, the bit resolution, the operational +mode (see below) and the power state of device and can read the current lux +value, of course. + + +Detection +--------- + +The ISL29003 does not have an ID register which could be used to identify +it, so the detection routine will just try to read from the configured I2C +addess and consider the device to be present as soon as it ACKs the +transfer. + + +Sysfs entries +------------- + +range: + 0: 0 lux to 1000 lux (default) + 1: 0 lux to 4000 lux + 2: 0 lux to 16,000 lux + 3: 0 lux to 64,000 lux + +resolution: + 0: 2^16 cycles (default) + 1: 2^12 cycles + 2: 2^8 cycles + 3: 2^4 cycles + +mode: + 0: diode1's current (unsigned 16bit) (default) + 1: diode1's current (unsigned 16bit) + 2: difference between diodes (l1 - l2, signed 15bit) + +power_state: + 0: device is disabled (default) + 1: device is enabled + +lux (read only): + returns the value from the last sensor reading + diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1c484084ed4f..5f3bff434621 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -223,6 +223,16 @@ config DELL_LAPTOP This driver adds support for rfkill and backlight control to Dell laptops. +config ISL29003 + tristate "Intersil ISL29003 ambient light sensor" + depends on I2C && SYSFS + help + If you say yes here you get support for the Intersil ISL29003 + ambient light sensor. + + This driver can also be built as a module. If so, the module + will be called isl29003. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index bc1199830554..7871f05dcb9b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -18,5 +18,6 @@ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o +obj-$(CONFIG_ISL29003) += isl29003.o obj-$(CONFIG_C2PORT) += c2port/ obj-y += eeprom/ diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c new file mode 100644 index 000000000000..2e2a5923d4c2 --- /dev/null +++ b/drivers/misc/isl29003.c @@ -0,0 +1,470 @@ +/* + * isl29003.c - Linux kernel module for + * Intersil ISL29003 ambient light sensor + * + * See file:Documentation/misc-devices/isl29003 + * + * Copyright (c) 2009 Daniel Mack + * + * Based on code written by + * Rodolfo Giometti + * Eurotech S.p.A. + * + * 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 +#include +#include +#include +#include +#include + +#define ISL29003_DRV_NAME "isl29003" +#define DRIVER_VERSION "1.0" + +#define ISL29003_REG_COMMAND 0x00 +#define ISL29003_ADC_ENABLED (1 << 7) +#define ISL29003_ADC_PD (1 << 6) +#define ISL29003_TIMING_INT (1 << 5) +#define ISL29003_MODE_SHIFT (2) +#define ISL29003_MODE_MASK (0x3 << ISL29003_MODE_SHIFT) +#define ISL29003_RES_SHIFT (0) +#define ISL29003_RES_MASK (0x3 << ISL29003_RES_SHIFT) + +#define ISL29003_REG_CONTROL 0x01 +#define ISL29003_INT_FLG (1 << 5) +#define ISL29003_RANGE_SHIFT (2) +#define ISL29003_RANGE_MASK (0x3 << ISL29003_RANGE_SHIFT) +#define ISL29003_INT_PERSISTS_SHIFT (0) +#define ISL29003_INT_PERSISTS_MASK (0xf << ISL29003_INT_PERSISTS_SHIFT) + +#define ISL29003_REG_IRQ_THRESH_HI 0x02 +#define ISL29003_REG_IRQ_THRESH_LO 0x03 +#define ISL29003_REG_LSB_SENSOR 0x04 +#define ISL29003_REG_MSB_SENSOR 0x05 +#define ISL29003_REG_LSB_TIMER 0x06 +#define ISL29003_REG_MSB_TIMER 0x07 + +#define ISL29003_NUM_CACHABLE_REGS 4 + +struct isl29003_data { + struct i2c_client *client; + struct mutex lock; + u8 reg_cache[ISL29003_NUM_CACHABLE_REGS]; +}; + +static int gain_range[] = { + 1000, 4000, 16000, 64000 +}; + +/* + * register access helpers + */ + +static int __isl29003_read_reg(struct i2c_client *client, + u32 reg, u8 mask, u8 shift) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + return (data->reg_cache[reg] & mask) >> shift; +} + +static int __isl29003_write_reg(struct i2c_client *client, + u32 reg, u8 mask, u8 shift, u8 val) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + int ret = 0; + u8 tmp; + + if (reg >= ISL29003_NUM_CACHABLE_REGS) + return -EINVAL; + + mutex_lock(&data->lock); + + tmp = data->reg_cache[reg]; + tmp &= ~mask; + tmp |= val << shift; + + ret = i2c_smbus_write_byte_data(client, reg, tmp); + if (!ret) + data->reg_cache[reg] = tmp; + + mutex_unlock(&data->lock); + return ret; +} + +/* + * internally used functions + */ + +/* range */ +static int isl29003_get_range(struct i2c_client *client) +{ + return __isl29003_read_reg(client, ISL29003_REG_CONTROL, + ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT); +} + +static int isl29003_set_range(struct i2c_client *client, int range) +{ + return __isl29003_write_reg(client, ISL29003_REG_CONTROL, + ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT, range); +} + +/* resolution */ +static int isl29003_get_resolution(struct i2c_client *client) +{ + return __isl29003_read_reg(client, ISL29003_REG_COMMAND, + ISL29003_RES_MASK, ISL29003_RES_SHIFT); +} + +static int isl29003_set_resolution(struct i2c_client *client, int res) +{ + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, + ISL29003_RES_MASK, ISL29003_RES_SHIFT, res); +} + +/* mode */ +static int isl29003_get_mode(struct i2c_client *client) +{ + return __isl29003_read_reg(client, ISL29003_REG_COMMAND, + ISL29003_RES_MASK, ISL29003_RES_SHIFT); +} + +static int isl29003_set_mode(struct i2c_client *client, int mode) +{ + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, + ISL29003_RES_MASK, ISL29003_RES_SHIFT, mode); +} + +/* power_state */ +static int isl29003_set_power_state(struct i2c_client *client, int state) +{ + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, + ISL29003_ADC_ENABLED | ISL29003_ADC_PD, 0, + state ? ISL29003_ADC_ENABLED : ISL29003_ADC_PD); +} + +static int isl29003_get_power_state(struct i2c_client *client) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + u8 cmdreg = data->reg_cache[ISL29003_REG_COMMAND]; + return ~cmdreg & ISL29003_ADC_PD; +} + +static int isl29003_get_adc_value(struct i2c_client *client) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + int lsb, msb, range, bitdepth; + + mutex_lock(&data->lock); + lsb = i2c_smbus_read_byte_data(client, ISL29003_REG_LSB_SENSOR); + + if (lsb < 0) { + mutex_unlock(&data->lock); + return lsb; + } + + msb = i2c_smbus_read_byte_data(client, ISL29003_REG_MSB_SENSOR); + mutex_unlock(&data->lock); + + if (msb < 0) + return msb; + + range = isl29003_get_range(client); + bitdepth = (4 - isl29003_get_resolution(client)) * 4; + return (((msb << 8) | lsb) * gain_range[range]) >> bitdepth; +} + +/* + * sysfs layer + */ + +/* range */ +static ssize_t isl29003_show_range(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%i\n", isl29003_get_range(client)); +} + +static ssize_t isl29003_store_range(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3)) + return -EINVAL; + + ret = isl29003_set_range(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, + isl29003_show_range, isl29003_store_range); + + +/* resolution */ +static ssize_t isl29003_show_resolution(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_resolution(client)); +} + +static ssize_t isl29003_store_resolution(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3)) + return -EINVAL; + + ret = isl29003_set_resolution(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO, + isl29003_show_resolution, isl29003_store_resolution); + +/* mode */ +static ssize_t isl29003_show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_mode(client)); +} + +static ssize_t isl29003_store_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 2)) + return -EINVAL; + + ret = isl29003_set_mode(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, + isl29003_show_mode, isl29003_store_mode); + + +/* power state */ +static ssize_t isl29003_show_power_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_power_state(client)); +} + +static ssize_t isl29003_store_power_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 1)) + return -EINVAL; + + ret = isl29003_set_power_state(client, val); + return ret ? ret : count; +} + +static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, + isl29003_show_power_state, isl29003_store_power_state); + + +/* lux */ +static ssize_t isl29003_show_lux(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + /* No LUX data if not operational */ + if (!isl29003_get_power_state(client)) + return -EBUSY; + + return sprintf(buf, "%d\n", isl29003_get_adc_value(client)); +} + +static DEVICE_ATTR(lux, S_IRUGO, isl29003_show_lux, NULL); + +static struct attribute *isl29003_attributes[] = { + &dev_attr_range.attr, + &dev_attr_resolution.attr, + &dev_attr_mode.attr, + &dev_attr_power_state.attr, + &dev_attr_lux.attr, + NULL +}; + +static const struct attribute_group isl29003_attr_group = { + .attrs = isl29003_attributes, +}; + +static int isl29003_init_client(struct i2c_client *client) +{ + struct isl29003_data *data = i2c_get_clientdata(client); + int i; + + /* read all the registers once to fill the cache. + * if one of the reads fails, we consider the init failed */ + for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) { + int v = i2c_smbus_read_byte_data(client, i); + if (v < 0) + return -ENODEV; + + data->reg_cache[i] = v; + } + + /* set defaults */ + isl29003_set_range(client, 0); + isl29003_set_resolution(client, 0); + isl29003_set_mode(client, 0); + isl29003_set_power_state(client, 0); + + return 0; +} + +/* + * I2C layer + */ + +static int __devinit isl29003_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct isl29003_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + /* initialize the ISL29003 chip */ + err = isl29003_init_client(client); + if (err) + goto exit_kfree; + + /* register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group); + if (err) + goto exit_kfree; + + dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION); + return 0; + +exit_kfree: + kfree(data); + return err; +} + +static int __devexit isl29003_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group); + isl29003_set_power_state(client, 0); + kfree(i2c_get_clientdata(client)); + return 0; +} + +#ifdef CONFIG_PM +static int isl29003_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return isl29003_set_power_state(client, 0); +} + +static int isl29003_resume(struct i2c_client *client) +{ + int i; + struct isl29003_data *data = i2c_get_clientdata(client); + + /* restore registers from cache */ + for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) + if (!i2c_smbus_write_byte_data(client, i, data->reg_cache[i])) + return -EIO; + + return 0; +} + +#else +#define isl29003_suspend NULL +#define isl29003_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id isl29003_id[] = { + { "isl29003", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, isl29003_id); + +static struct i2c_driver isl29003_driver = { + .driver = { + .name = ISL29003_DRV_NAME, + .owner = THIS_MODULE, + }, + .suspend = isl29003_suspend, + .resume = isl29003_resume, + .probe = isl29003_probe, + .remove = __devexit_p(isl29003_remove), + .id_table = isl29003_id, +}; + +static int __init isl29003_init(void) +{ + return i2c_add_driver(&isl29003_driver); +} + +static void __exit isl29003_exit(void) +{ + i2c_del_driver(&isl29003_driver); +} + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("ISL29003 ambient light sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRIVER_VERSION); + +module_init(isl29003_init); +module_exit(isl29003_exit); + -- cgit v1.2.3-59-g8ed1b From 2b872903c5d66bccdf4306a35e3e94d4da8555d3 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 31 Mar 2009 15:24:25 -0700 Subject: hp_accel: small documentation updates Fix english in Documentation, add "how to test" description. Signed-off-by: Pavel Machek Cc: Eric Piel Cc: Vladimir Botka Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/hwmon/lis3lv02d | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lis3lv02d b/Documentation/hwmon/lis3lv02d index 287f8c902656..effe949a7282 100644 --- a/Documentation/hwmon/lis3lv02d +++ b/Documentation/hwmon/lis3lv02d @@ -1,11 +1,11 @@ Kernel driver lis3lv02d -================== +======================= Supported chips: * STMicroelectronics LIS3LV02DL and LIS3LV02DQ -Author: +Authors: Yan Burman Eric Piel @@ -15,7 +15,7 @@ Description This driver provides support for the accelerometer found in various HP laptops sporting the feature officially called "HP Mobile Data -Protection System 3D" or "HP 3D DriveGuard". It detect automatically +Protection System 3D" or "HP 3D DriveGuard". It detects automatically laptops with this sensor. Known models (for now the HP 2133, nc6420, nc2510, nc8510, nc84x0, nw9440 and nx9420) will have their axis automatically oriented on standard way (eg: you can directly play @@ -27,7 +27,7 @@ position - 3D position that the accelerometer reports. Format: "(x,y,z)" calibrate - read: values (x, y, z) that are used as the base for input class device operation. write: forces the base to be recalibrated with the current - position. + position. rate - reports the sampling rate of the accelerometer device in HZ This driver also provides an absolute input class device, allowing @@ -48,7 +48,7 @@ For better compatibility between the various laptops. The values reported by the accelerometer are converted into a "standard" organisation of the axes (aka "can play neverball out of the box"): * When the laptop is horizontal the position reported is about 0 for X and Y -and a positive value for Z + and a positive value for Z * If the left side is elevated, X increases (becomes positive) * If the front side (where the touchpad is) is elevated, Y decreases (becomes negative) @@ -59,3 +59,13 @@ email to the authors to add it to the database. When reporting a new laptop, please include the output of "dmidecode" plus the value of /sys/devices/platform/lis3lv02d/position in these four cases. +Q&A +--- + +Q: How do I safely simulate freefall? I have an HP "portable +workstation" which has about 3.5kg and a plastic case, so letting it +fall to the ground is out of question... + +A: The sensor is pretty sensitive, so your hands can do it. Lift it +into free space, follow the fall with your hands for like 10 +centimeters. That should be enough to trigger the detection. -- cgit v1.2.3-59-g8ed1b From 72f5de92e199f96cfcea125aefc76c138d8c553c Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Tue, 31 Mar 2009 15:24:29 -0700 Subject: hwmon: Add LTC4215 driver Add Linux support for the Linear Technology LTC4215 Hot Swap controller I2C monitoring interface. I have tested the driver with my board, and it appears to work fine. With the power supplies disabled, it reads 11.93V input, 1.93V output, no current and no power. With the supplies enabled, it reads 11.93V input, 11.98V output, no current, no power. I'm not drawing any current at the moment, so this is reasonable. The value in the sense register never reads anything except 0, so I expect to get zero from the current and power calculations. I didn't attempt to support changing any of the chip's settings or enabling the FET. I'm not sure even how to do that and still fit within the hwmon framework. :) Signed-off-by: Ira W. Snyder Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/hwmon/ltc4215 | 50 ++++++ drivers/hwmon/Kconfig | 11 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc4215.c | 364 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 426 insertions(+) create mode 100644 Documentation/hwmon/ltc4215 create mode 100644 drivers/hwmon/ltc4215.c (limited to 'Documentation') diff --git a/Documentation/hwmon/ltc4215 b/Documentation/hwmon/ltc4215 new file mode 100644 index 000000000000..2e6a21eb656c --- /dev/null +++ b/Documentation/hwmon/ltc4215 @@ -0,0 +1,50 @@ +Kernel driver ltc4215 +===================== + +Supported chips: + * Linear Technology LTC4215 + Prefix: 'ltc4215' + Addresses scanned: 0x44 + Datasheet: + http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697 + +Author: Ira W. Snyder + + +Description +----------- + +The LTC4215 controller allows a board to be safely inserted and removed +from a live backplane. + + +Usage Notes +----------- + +This driver does not probe for LTC4215 devices, due to the fact that some +of the possible addresses are unfriendly to probing. You will need to use +the "force" parameter to tell the driver where to find the device. + +Example: the following will load the driver for an LTC4215 at address 0x44 +on I2C bus #0: +$ modprobe ltc4215 force=0,0x44 + + +Sysfs entries +------------- + +The LTC4215 has built-in limits for overvoltage, undervoltage, and +undercurrent warnings. This makes it very likely that the reference +circuit will be used. + +in1_input input voltage +in2_input output voltage + +in1_min_alarm input undervoltage alarm +in1_max_alarm input overvoltage alarm + +curr1_input current +curr1_max_alarm overcurrent alarm + +power1_input power usage +power1_alarm power bad alarm diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8a91c9971558..bc6810c22dd7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -571,6 +571,17 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_LTC4215 + tristate "Linear Technology LTC4215" + depends on I2C && EXPERIMENTAL + default n + help + If you say yes here you get support for Linear Technology LTC4215 + Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4215. + config SENSORS_LTC4245 tristate "Linear Technology LTC4245" depends on I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 81c88822a3eb..4da261d16abf 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o +obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c new file mode 100644 index 000000000000..9386e2a39211 --- /dev/null +++ b/drivers/hwmon/ltc4215.c @@ -0,0 +1,364 @@ +/* + * Driver for Linear Technology LTC4215 I2C Hot Swap Controller + * + * Copyright (C) 2009 Ira W. Snyder + * + * 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; version 2 of the License. + * + * Datasheet: + * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(ltc4215); + +/* Here are names of the chip's registers (a.k.a. commands) */ +enum ltc4215_cmd { + LTC4215_CONTROL = 0x00, /* rw */ + LTC4215_ALERT = 0x01, /* rw */ + LTC4215_STATUS = 0x02, /* ro */ + LTC4215_FAULT = 0x03, /* rw */ + LTC4215_SENSE = 0x04, /* rw */ + LTC4215_SOURCE = 0x05, /* rw */ + LTC4215_ADIN = 0x06, /* rw */ +}; + +struct ltc4215_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Registers */ + u8 regs[7]; +}; + +static struct ltc4215_data *ltc4215_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc4215_data *data = i2c_get_clientdata(client); + s32 val; + int i; + + mutex_lock(&data->update_lock); + + /* The chip's A/D updates 10 times per second */ + if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) { + + dev_dbg(&client->dev, "Starting ltc4215 update\n"); + + /* Read all registers */ + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { + val = i2c_smbus_read_byte_data(client, i); + if (unlikely(val < 0)) + data->regs[i] = 0; + else + data->regs[i] = val; + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* Return the voltage from the given register in millivolts */ +static int ltc4215_get_voltage(struct device *dev, u8 reg) +{ + struct ltc4215_data *data = ltc4215_update_device(dev); + const u8 regval = data->regs[reg]; + u32 voltage = 0; + + switch (reg) { + case LTC4215_SENSE: + /* 151 uV per increment */ + voltage = regval * 151 / 1000; + break; + case LTC4215_SOURCE: + /* 60.5 mV per increment */ + voltage = regval * 605 / 10; + break; + case LTC4215_ADIN: + /* The ADIN input is divided by 12.5, and has 4.82 mV + * per increment, so we have the additional multiply */ + voltage = regval * 482 * 125 / 1000; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + break; + } + + return voltage; +} + +/* Return the current from the sense resistor in mA */ +static unsigned int ltc4215_get_current(struct device *dev) +{ + struct ltc4215_data *data = ltc4215_update_device(dev); + + /* The strange looking conversions that follow are fixed-point + * math, since we cannot do floating point in the kernel. + * + * Step 1: convert sense register to microVolts + * Step 2: convert voltage to milliAmperes + * + * If you play around with the V=IR equation, you come up with + * the following: X uV / Y mOhm == Z mA + * + * With the resistors that are fractions of a milliOhm, we multiply + * the voltage and resistance by 10, to shift the decimal point. + * Now we can use the normal division operator again. + */ + + /* Calculate voltage in microVolts (151 uV per increment) */ + const unsigned int voltage = data->regs[LTC4215_SENSE] * 151; + + /* Calculate current in milliAmperes (4 milliOhm sense resistor) */ + const unsigned int curr = voltage / 4; + + return curr; +} + +static ssize_t ltc4215_show_voltage(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + const int voltage = ltc4215_get_voltage(dev, attr->index); + + return snprintf(buf, PAGE_SIZE, "%d\n", voltage); +} + +static ssize_t ltc4215_show_current(struct device *dev, + struct device_attribute *da, + char *buf) +{ + const unsigned int curr = ltc4215_get_current(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", curr); +} + +static ssize_t ltc4215_show_power(struct device *dev, + struct device_attribute *da, + char *buf) +{ + const unsigned int curr = ltc4215_get_current(dev); + const int output_voltage = ltc4215_get_voltage(dev, LTC4215_ADIN); + + /* current in mA * voltage in mV == power in uW */ + const unsigned int power = abs(output_voltage * curr); + + return snprintf(buf, PAGE_SIZE, "%u\n", power); +} + +static ssize_t ltc4215_show_alarm(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct ltc4215_data *data = ltc4215_update_device(dev); + const u8 reg = data->regs[attr->index]; + const u32 mask = attr->nr; + + return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); +} + +/* These macros are used below in constructing device attribute objects + * for use with sysfs_create_group() to make a sysfs device file + * for each register. + */ + +#define LTC4215_VOLTAGE(name, ltc4215_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4215_show_voltage, NULL, ltc4215_cmd_idx) + +#define LTC4215_CURRENT(name) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4215_show_current, NULL, 0); + +#define LTC4215_POWER(name) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4215_show_power, NULL, 0); + +#define LTC4215_ALARM(name, mask, reg) \ + static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ + ltc4215_show_alarm, NULL, (mask), reg) + +/* Construct a sensor_device_attribute structure for each register */ + +/* Current */ +LTC4215_CURRENT(curr1_input); +LTC4215_ALARM(curr1_max_alarm, (1 << 2), LTC4215_STATUS); + +/* Power (virtual) */ +LTC4215_POWER(power1_input); +LTC4215_ALARM(power1_alarm, (1 << 3), LTC4215_STATUS); + +/* Input Voltage */ +LTC4215_VOLTAGE(in1_input, LTC4215_ADIN); +LTC4215_ALARM(in1_max_alarm, (1 << 0), LTC4215_STATUS); +LTC4215_ALARM(in1_min_alarm, (1 << 1), LTC4215_STATUS); + +/* Output Voltage */ +LTC4215_VOLTAGE(in2_input, LTC4215_SOURCE); + +/* Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *ltc4215_attributes[] = { + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_alarm.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_input.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ltc4215_group = { + .attrs = ltc4215_attributes, +}; + +static int ltc4215_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ltc4215_data *data; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out_kzalloc; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the LTC4215 chip */ + /* TODO */ + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, <c4215_group); + if (ret) + goto out_sysfs_create_group; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_hwmon_device_register; + } + + return 0; + +out_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, <c4215_group); +out_sysfs_create_group: + kfree(data); +out_kzalloc: + return ret; +} + +static int ltc4215_remove(struct i2c_client *client) +{ + struct ltc4215_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, <c4215_group); + + kfree(data); + + return 0; +} + +static int ltc4215_detect(struct i2c_client *client, + int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (kind < 0) { /* probed detection - check the chip type */ + s32 v; /* 8 bits from the chip, or -ERRNO */ + + /* + * Register 0x01 bit b7 is reserved, expect 0 + * Register 0x03 bit b6 and b7 are reserved, expect 0 + */ + v = i2c_smbus_read_byte_data(client, LTC4215_ALERT); + if (v < 0 || (v & (1 << 7)) != 0) + return -ENODEV; + + v = i2c_smbus_read_byte_data(client, LTC4215_FAULT); + if (v < 0 || (v & ((1 << 6) | (1 << 7))) != 0) + return -ENODEV; + } + + strlcpy(info->type, "ltc4215", I2C_NAME_SIZE); + dev_info(&adapter->dev, "ltc4215 %s at address 0x%02x\n", + kind < 0 ? "probed" : "forced", + client->addr); + + return 0; +} + +static const struct i2c_device_id ltc4215_id[] = { + { "ltc4215", ltc4215 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4215_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4215_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ltc4215", + }, + .probe = ltc4215_probe, + .remove = ltc4215_remove, + .id_table = ltc4215_id, + .detect = ltc4215_detect, + .address_data = &addr_data, +}; + +static int __init ltc4215_init(void) +{ + return i2c_add_driver(<c4215_driver); +} + +static void __exit ltc4215_exit(void) +{ + i2c_del_driver(<c4215_driver); +} + +MODULE_AUTHOR("Ira W. Snyder "); +MODULE_DESCRIPTION("LTC4215 driver"); +MODULE_LICENSE("GPL"); + +module_init(ltc4215_init); +module_exit(ltc4215_exit); -- cgit v1.2.3-59-g8ed1b From 3f1c6ebf57b815ad709e89291e446935fee78f75 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 31 Mar 2009 15:24:38 -0700 Subject: powerpc: add mmc-spi-slot bindings The bindings describes a case where MMC/SD/SDIO slot directly connected to a SPI bus. Such setups are widely used on embedded PowerPC boards. The patch also adds the mmc-spi-slot entry to the OpenFirmware modalias table. Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: Benjamin Herrenschmidt Cc: Kumar Gala Cc: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../powerpc/dts-bindings/mmc-spi-slot.txt | 23 ++++++++++++++++++++++ drivers/of/base.c | 1 + 2 files changed, 24 insertions(+) create mode 100644 Documentation/powerpc/dts-bindings/mmc-spi-slot.txt (limited to 'Documentation') diff --git a/Documentation/powerpc/dts-bindings/mmc-spi-slot.txt b/Documentation/powerpc/dts-bindings/mmc-spi-slot.txt new file mode 100644 index 000000000000..c39ac2891951 --- /dev/null +++ b/Documentation/powerpc/dts-bindings/mmc-spi-slot.txt @@ -0,0 +1,23 @@ +MMC/SD/SDIO slot directly connected to a SPI bus + +Required properties: +- compatible : should be "mmc-spi-slot". +- reg : should specify SPI address (chip-select number). +- spi-max-frequency : maximum frequency for this device (Hz). +- voltage-ranges : two cells are required, first cell specifies minimum + slot voltage (mV), second cell specifies maximum slot voltage (mV). + Several ranges could be specified. +- gpios : (optional) may specify GPIOs in this order: Card-Detect GPIO, + Write-Protect GPIO. + +Example: + + mmc-slot@0 { + compatible = "fsl,mpc8323rdb-mmc-slot", + "mmc-spi-slot"; + reg = <0>; + gpios = <&qe_pio_d 14 1 + &qe_pio_d 15 0>; + voltage-ranges = <3300 3300>; + spi-max-frequency = <50000000>; + }; diff --git a/drivers/of/base.c b/drivers/of/base.c index cd17092b82bd..41c5dfd85358 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -446,6 +446,7 @@ struct of_modalias_table { }; static struct of_modalias_table of_modalias_table[] = { { "fsl,mcu-mpc8349emitx", "mcu-mpc8349emitx" }, + { "mmc-spi-slot", "mmc_spi" }, }; /** -- cgit v1.2.3-59-g8ed1b From ddb53d48da5b0e691f35e703ac29118747f86c99 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Tue, 31 Mar 2009 15:25:40 -0700 Subject: fbdev: remove cyblafb driver A tridentfb driver has all the functionality of the cyblafb driver without the bugs of the latter. Changes to the tridentfb driver: - FBINFO_READS_FAST added to the tridentfb. The cyblafb used a blitter for scrolling which is faster than color expansion on Cyberblade chipsets. The blitter is slower on a discrete Blade3D core. Use the blitter for scrolling in the tridentfb only for integrated Blade3D cores. Now, scrolling speed is about equal for the tridentfb and the cyblafb. - a copyright notice addition is done on request of Jani Monoses (the first author of the tridentfb). Tested on AGP Blade3D card and PCChips M787CLR motherboard: VIA C3 cpu + VT8601 north bridge (aka Cyberblade/i1). Signed-off-by: Krzysztof Helt Cc: "Jani Monoses" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/fb/00-INDEX | 2 - Documentation/fb/cyblafb/bugs | 13 - Documentation/fb/cyblafb/credits | 7 - Documentation/fb/cyblafb/documentation | 17 - Documentation/fb/cyblafb/fb.modes | 154 --- Documentation/fb/cyblafb/performance | 79 -- Documentation/fb/cyblafb/todo | 31 - Documentation/fb/cyblafb/usage | 217 ---- Documentation/fb/cyblafb/whatsnew | 29 - Documentation/fb/cyblafb/whycyblafb | 85 -- drivers/video/Kconfig | 28 +- drivers/video/cyblafb.c | 1683 -------------------------------- drivers/video/tridentfb.c | 6 +- 13 files changed, 7 insertions(+), 2344 deletions(-) delete mode 100644 Documentation/fb/cyblafb/bugs delete mode 100644 Documentation/fb/cyblafb/credits delete mode 100644 Documentation/fb/cyblafb/documentation delete mode 100644 Documentation/fb/cyblafb/fb.modes delete mode 100644 Documentation/fb/cyblafb/performance delete mode 100644 Documentation/fb/cyblafb/todo delete mode 100644 Documentation/fb/cyblafb/usage delete mode 100644 Documentation/fb/cyblafb/whatsnew delete mode 100644 Documentation/fb/cyblafb/whycyblafb delete mode 100644 drivers/video/cyblafb.c (limited to 'Documentation') diff --git a/Documentation/fb/00-INDEX b/Documentation/fb/00-INDEX index caabbd395e61..a618fd99c9f0 100644 --- a/Documentation/fb/00-INDEX +++ b/Documentation/fb/00-INDEX @@ -11,8 +11,6 @@ aty128fb.txt - info on the ATI Rage128 frame buffer driver. cirrusfb.txt - info on the driver for Cirrus Logic chipsets. -cyblafb/ - - directory with documentation files related to the cyblafb driver. deferred_io.txt - an introduction to deferred IO. fbcon.txt diff --git a/Documentation/fb/cyblafb/bugs b/Documentation/fb/cyblafb/bugs deleted file mode 100644 index 9443a6d72cdd..000000000000 --- a/Documentation/fb/cyblafb/bugs +++ /dev/null @@ -1,13 +0,0 @@ -Bugs -==== - -I currently don't know of any bug. Please do send reports to: - - linux-fbdev-devel@lists.sourceforge.net - - Knut_Petersen@t-online.de. - - -Untested features -================= - -All LCD stuff is untested. If it worked in tridentfb, it should work in -cyblafb. Please test and report the results to Knut_Petersen@t-online.de. diff --git a/Documentation/fb/cyblafb/credits b/Documentation/fb/cyblafb/credits deleted file mode 100644 index 0eb3b443dc2b..000000000000 --- a/Documentation/fb/cyblafb/credits +++ /dev/null @@ -1,7 +0,0 @@ -Thanks to -========= - * Alan Hourihane, for writing the X trident driver - * Jani Monoses, for writing the tridentfb driver - * Antonino A. Daplas, for review of the first published - version of cyblafb and some code - * Jochen Hein, for testing and a helpfull bug report diff --git a/Documentation/fb/cyblafb/documentation b/Documentation/fb/cyblafb/documentation deleted file mode 100644 index bb1aac048425..000000000000 --- a/Documentation/fb/cyblafb/documentation +++ /dev/null @@ -1,17 +0,0 @@ -Available Documentation -======================= - -Apollo PLE 133 Chipset VT8601A North Bridge Datasheet, Rev. 1.82, October 22, -2001, available from VIA: - - http://www.viavpsd.com/product/6/15/DS8601A182.pdf - -The datasheet is incomplete, some registers that need to be programmed are not -explained at all and important bits are listed as "reserved". But you really -need the datasheet to understand the code. "p. xxx" comments refer to page -numbers of this document. - -XFree/XOrg drivers are available and of good quality, looking at the code -there is a good idea if the datasheet does not provide enough information -or if the datasheet seems to be wrong. - diff --git a/Documentation/fb/cyblafb/fb.modes b/Documentation/fb/cyblafb/fb.modes deleted file mode 100644 index fe0e5223ba86..000000000000 --- a/Documentation/fb/cyblafb/fb.modes +++ /dev/null @@ -1,154 +0,0 @@ -# -# Sample fb.modes file -# -# Provides an incomplete list of working modes for -# the cyberblade/i1 graphics core. -# -# The value 4294967256 is used instead of -40. Of course, -40 is not -# a really reasonable value, but chip design does not always follow -# logic. Believe me, it's ok, and it's the way the BIOS does it. -# -# fbset requires 4294967256 in fb.modes and -40 as an argument to -# the -t parameter. That's also not too reasonable, and it might change -# in the future or might even be differt for your current version. -# - -mode "640x480-50" - geometry 640 480 2048 4096 8 - timings 47619 4294967256 24 17 0 216 3 -endmode - -mode "640x480-60" - geometry 640 480 2048 4096 8 - timings 39682 4294967256 24 17 0 216 3 -endmode - -mode "640x480-70" - geometry 640 480 2048 4096 8 - timings 34013 4294967256 24 17 0 216 3 -endmode - -mode "640x480-72" - geometry 640 480 2048 4096 8 - timings 33068 4294967256 24 17 0 216 3 -endmode - -mode "640x480-75" - geometry 640 480 2048 4096 8 - timings 31746 4294967256 24 17 0 216 3 -endmode - -mode "640x480-80" - geometry 640 480 2048 4096 8 - timings 29761 4294967256 24 17 0 216 3 -endmode - -mode "640x480-85" - geometry 640 480 2048 4096 8 - timings 28011 4294967256 24 17 0 216 3 -endmode - -mode "800x600-50" - geometry 800 600 2048 4096 8 - timings 30303 96 24 14 0 136 11 -endmode - -mode "800x600-60" - geometry 800 600 2048 4096 8 - timings 25252 96 24 14 0 136 11 -endmode - -mode "800x600-70" - geometry 800 600 2048 4096 8 - timings 21645 96 24 14 0 136 11 -endmode - -mode "800x600-72" - geometry 800 600 2048 4096 8 - timings 21043 96 24 14 0 136 11 -endmode - -mode "800x600-75" - geometry 800 600 2048 4096 8 - timings 20202 96 24 14 0 136 11 -endmode - -mode "800x600-80" - geometry 800 600 2048 4096 8 - timings 18939 96 24 14 0 136 11 -endmode - -mode "800x600-85" - geometry 800 600 2048 4096 8 - timings 17825 96 24 14 0 136 11 -endmode - -mode "1024x768-50" - geometry 1024 768 2048 4096 8 - timings 19054 144 24 29 0 120 3 -endmode - -mode "1024x768-60" - geometry 1024 768 2048 4096 8 - timings 15880 144 24 29 0 120 3 -endmode - -mode "1024x768-70" - geometry 1024 768 2048 4096 8 - timings 13610 144 24 29 0 120 3 -endmode - -mode "1024x768-72" - geometry 1024 768 2048 4096 8 - timings 13232 144 24 29 0 120 3 -endmode - -mode "1024x768-75" - geometry 1024 768 2048 4096 8 - timings 12703 144 24 29 0 120 3 -endmode - -mode "1024x768-80" - geometry 1024 768 2048 4096 8 - timings 11910 144 24 29 0 120 3 -endmode - -mode "1024x768-85" - geometry 1024 768 2048 4096 8 - timings 11209 144 24 29 0 120 3 -endmode - -mode "1280x1024-50" - geometry 1280 1024 2048 4096 8 - timings 11114 232 16 39 0 160 3 -endmode - -mode "1280x1024-60" - geometry 1280 1024 2048 4096 8 - timings 9262 232 16 39 0 160 3 -endmode - -mode "1280x1024-70" - geometry 1280 1024 2048 4096 8 - timings 7939 232 16 39 0 160 3 -endmode - -mode "1280x1024-72" - geometry 1280 1024 2048 4096 8 - timings 7719 232 16 39 0 160 3 -endmode - -mode "1280x1024-75" - geometry 1280 1024 2048 4096 8 - timings 7410 232 16 39 0 160 3 -endmode - -mode "1280x1024-80" - geometry 1280 1024 2048 4096 8 - timings 6946 232 16 39 0 160 3 -endmode - -mode "1280x1024-85" - geometry 1280 1024 2048 4096 8 - timings 6538 232 16 39 0 160 3 -endmode diff --git a/Documentation/fb/cyblafb/performance b/Documentation/fb/cyblafb/performance deleted file mode 100644 index 8d15d5dfc6b3..000000000000 --- a/Documentation/fb/cyblafb/performance +++ /dev/null @@ -1,79 +0,0 @@ -Speed -===== - -CyBlaFB is much faster than tridentfb and vesafb. Compare the performance data -for mode 1280x1024-[8,16,32]@61 Hz. - -Test 1: Cat a file with 2000 lines of 0 characters. -Test 2: Cat a file with 2000 lines of 80 characters. -Test 3: Cat a file with 2000 lines of 160 characters. - -All values show system time use in seconds, kernel 2.6.12 was used for -the measurements. 2.6.13 is a bit slower, 2.6.14 hopefully will include a -patch that speeds up kernel bitblitting a lot ( > 20%). - -+-----------+-----------------------------------------------------+ -| | not accelerated | -| TRIDENTFB +-----------------+-----------------+-----------------+ -| of 2.6.12 | 8 bpp | 16 bpp | 32 bpp | -| | noypan | ypan | noypan | ypan | noypan | ypan | -+-----------+--------+--------+--------+--------+--------+--------+ -| Test 1 | 4.31 | 4.33 | 6.05 | 12.81 | ---- | ---- | -| Test 2 | 67.94 | 5.44 | 123.16 | 14.79 | ---- | ---- | -| Test 3 | 131.36 | 6.55 | 240.12 | 16.76 | ---- | ---- | -+-----------+--------+--------+--------+--------+--------+--------+ -| Comments | | | completely bro- | -| | | | ken, monitor | -| | | | switches off | -+-----------+-----------------+-----------------+-----------------+ - - -+-----------+-----------------------------------------------------+ -| | accelerated | -| TRIDENTFB +-----------------+-----------------+-----------------+ -| of 2.6.12 | 8 bpp | 16 bpp | 32 bpp | -| | noypan | ypan | noypan | ypan | noypan | ypan | -+-----------+--------+--------+--------+--------+--------+--------+ -| Test 1 | ---- | ---- | 20.62 | 1.22 | ---- | ---- | -| Test 2 | ---- | ---- | 22.61 | 3.19 | ---- | ---- | -| Test 3 | ---- | ---- | 24.59 | 5.16 | ---- | ---- | -+-----------+--------+--------+--------+--------+--------+--------+ -| Comments | broken, writing | broken, ok only | completely bro- | -| | to wrong places | if bgcolor is | ken, monitor | -| | on screen + bug | black, bug in | switches off | -| | in fillrect() | fillrect() | | -+-----------+-----------------+-----------------+-----------------+ - - -+-----------+-----------------------------------------------------+ -| | not accelerated | -| VESAFB +-----------------+-----------------+-----------------+ -| of 2.6.12 | 8 bpp | 16 bpp | 32 bpp | -| | noypan | ypan | noypan | ypan | noypan | ypan | -+-----------+--------+--------+--------+--------+--------+--------+ -| Test 1 | 4.26 | 3.76 | 5.99 | 7.23 | ---- | ---- | -| Test 2 | 65.65 | 4.89 | 120.88 | 9.08 | ---- | ---- | -| Test 3 | 126.91 | 5.94 | 235.77 | 11.03 | ---- | ---- | -+-----------+--------+--------+--------+--------+--------+--------+ -| Comments | vga=0x307 | vga=0x31a | vga=0x31b not | -| | fh=80kHz | fh=80kHz | supported by | -| | fv=75kHz | fv=75kHz | video BIOS and | -| | | | hardware | -+-----------+-----------------+-----------------+-----------------+ - - -+-----------+-----------------------------------------------------+ -| | accelerated | -| CYBLAFB +-----------------+-----------------+-----------------+ -| | 8 bpp | 16 bpp | 32 bpp | -| | noypan | ypan | noypan | ypan | noypan | ypan | -+-----------+--------+--------+--------+--------+--------+--------+ -| Test 1 | 8.02 | 0.23 | 19.04 | 0.61 | 57.12 | 2.74 | -| Test 2 | 8.38 | 0.55 | 19.39 | 0.92 | 57.54 | 3.13 | -| Test 3 | 8.73 | 0.86 | 19.74 | 1.24 | 57.95 | 3.51 | -+-----------+--------+--------+--------+--------+--------+--------+ -| Comments | | | | -| | | | | -| | | | | -| | | | | -+-----------+-----------------+-----------------+-----------------+ diff --git a/Documentation/fb/cyblafb/todo b/Documentation/fb/cyblafb/todo deleted file mode 100644 index c5f6d0eae545..000000000000 --- a/Documentation/fb/cyblafb/todo +++ /dev/null @@ -1,31 +0,0 @@ -TODO / Missing features -======================= - -Verify LCD stuff "stretch" and "center" options are - completely untested ... this code needs to be - verified. As I don't have access to such - hardware, please contact me if you are - willing run some tests. - -Interlaced video modes The reason that interleaved - modes are disabled is that I do not know - the meaning of the vertical interlace - parameter. Also the datasheet mentions a - bit d8 of a horizontal interlace parameter, - but nowhere the lower 8 bits. Please help - if you can. - -low-res double scan modes Who needs it? - -accelerated color blitting Who needs it? The console driver does use color - blitting for nothing but drawing the penguine, - everything else is done using color expanding - blitting of 1bpp character bitmaps. - -ioctls Who needs it? - -TV-out Will be done later. Use "vga= " at boot time - to set a suitable video mode. - -??? Feel free to contact me if you have any - feature requests diff --git a/Documentation/fb/cyblafb/usage b/Documentation/fb/cyblafb/usage deleted file mode 100644 index a39bb3d402a2..000000000000 --- a/Documentation/fb/cyblafb/usage +++ /dev/null @@ -1,217 +0,0 @@ -CyBlaFB is a framebuffer driver for the Cyberblade/i1 graphics core integrated -into the VIA Apollo PLE133 (aka vt8601) south bridge. It is developed and -tested using a VIA EPIA 5000 board. - -Cyblafb - compiled into the kernel or as a module? -================================================== - -You might compile cyblafb either as a module or compile it permanently into the -kernel. - -Unless you have a real reason to do so you should not compile both vesafb and -cyblafb permanently into the kernel. It's possible and it helps during the -developement cycle, but it's useless and will at least block some otherwise -usefull memory for ordinary users. - -Selecting Modes -=============== - - Startup Mode - ============ - - First of all, you might use the "vga=???" boot parameter as it is - documented in vesafb.txt and svga.txt. Cyblafb will detect the video - mode selected and will use the geometry and timings found by - inspecting the hardware registers. - - video=cyblafb vga=0x317 - - Alternatively you might use a combination of the mode, ref and bpp - parameters. If you compiled the driver into the kernel, add something - like this to the kernel command line: - - video=cyblafb:1280x1024,bpp=16,ref=50 ... - - If you compiled the driver as a module, the same mode would be - selected by the following command: - - modprobe cyblafb mode=1280x1024 bpp=16 ref=50 ... - - None of the modes possible to select as startup modes are affected by - the problems described at the end of the next subsection. - - For all startup modes cyblafb chooses a virtual x resolution of 2048, - the only exception is mode 1280x1024 in combination with 32 bpp. This - allows ywrap scrolling for all those modes if rotation is 0 or 2, and - also fast scrolling if rotation is 1 or 3. The default virtual y reso- - lution is 4096 for bpp == 8, 2048 for bpp==16 and 1024 for bpp == 32, - again with the only exception of 1280x1024 at 32 bpp. - - Please do set your video memory size to 8 Mb in the Bios setup. Other - values will work, but performace is decreased for a lot of modes. - - Mode changes using fbset - ======================== - - You might use fbset to change the video mode, see "man fbset". Cyblafb - generally does assume that you know what you are doing. But it does - some checks, especially those that are needed to prevent you from - damaging your hardware. - - - only 8, 16, 24 and 32 bpp video modes are accepted - - interlaced video modes are not accepted - - double scan video modes are not accepted - - if a flat panel is found, cyblafb does not allow you - to program a resolution higher than the physical - resolution of the flat panel monitor - - cyblafb does not allow vclk to exceed 230 MHz. As 32 bpp - and (currently) 24 bit modes use a doubled vclk internally, - the dotclock limit as seen by fbset is 115 MHz for those - modes and 230 MHz for 8 and 16 bpp modes. - - cyblafb will allow you to select very high resolutions as - long as the hardware can be programmed to these modes. The - documented limit 1600x1200 is not enforced, but don't expect - perfect signal quality. - - Any request that violates the rules given above will be either changed - to something the hardware supports or an error value will be returned. - - If you program a virtual y resolution higher than the hardware limit, - cyblafb will silently decrease that value to the highest possible - value. The same is true for a virtual x resolution that is not - supported by the hardware. Cyblafb tries to adapt vyres first because - vxres decides if ywrap scrolling is possible or not. - - Attempts to disable acceleration are ignored, I believe that this is - safe. - - Some video modes that should work do not work as expected. If you use - the standard fb.modes, fbset 640x480-60 will program that mode, but - you will see a vertical area, about two characters wide, with only - much darker characters than the other characters on the screen. - Cyblafb does allow that mode to be set, as it does not violate the - official specifications. It would need a lot of code to reliably sort - out all invalid modes, playing around with the margin values will - give a valid mode quickly. And if cyblafb would detect such an invalid - mode, should it silently alter the requested values or should it - report an error? Both options have some pros and cons. As stated - above, none of the startup modes are affected, and if you set - verbosity to 1 or higher, cyblafb will print the fbset command that - would be needed to program that mode using fbset. - - -Other Parameters -================ - - -crt don't autodetect, assume monitor connected to - standard VGA connector - -fp don't autodetect, assume flat panel display - connected to flat panel monitor interface - -nativex inform driver about native x resolution of - flat panel monitor connected to special - interface (should be autodetected) - -stretch stretch image to adapt low resolution modes to - higer resolutions of flat panel monitors - connected to special interface - -center center image to adapt low resolution modes to - higer resolutions of flat panel monitors - connected to special interface - -memsize use if autodetected memsize is wrong ... - should never be necessary - -nopcirr disable PCI read retry -nopciwr disable PCI write retry -nopcirb disable PCI read bursts -nopciwb disable PCI write bursts - -bpp bpp for specified modes - valid values: 8 || 16 || 24 || 32 - -ref refresh rate for specified mode - valid values: 50 <= ref <= 85 - -mode 640x480 or 800x600 or 1024x768 or 1280x1024 - if not specified, the startup mode will be detected - and used, so you might also use the vga=??? parameter - described in vesafb.txt. If you do not specify a mode, - bpp and ref parameters are ignored. - -verbosity 0 is the default, increase to at least 2 for every - bug report! - -Development hints -================= - -It's much faster do compile a module and to load the new version after -unloading the old module than to compile a new kernel and to reboot. So if you -try to work on cyblafb, it might be a good idea to use cyblafb as a module. -In real life, fast often means dangerous, and that's also the case here. If -you introduce a serious bug when cyblafb is compiled into the kernel, the -kernel will lock or oops with a high probability before the file system is -mounted, and the danger for your data is low. If you load a broken own version -of cyblafb on a running system, the danger for the integrity of the file -system is much higher as you might need a hard reset afterwards. Decide -yourself. - -Module unloading, the vfb method -================================ - -If you want to unload/reload cyblafb using the virtual framebuffer, you need -to enable vfb support in the kernel first. After that, load the modules as -shown below: - - modprobe vfb vfb_enable=1 - modprobe fbcon - modprobe cyblafb - fbset -fb /dev/fb1 1280x1024-60 -vyres 2662 - con2fb /dev/fb1 /dev/tty1 - ... - -If you now made some changes to cyblafb and want to reload it, you might do it -as show below: - - con2fb /dev/fb0 /dev/tty1 - ... - rmmod cyblafb - modprobe cyblafb - con2fb /dev/fb1 /dev/tty1 - ... - -Of course, you might choose another mode, and most certainly you also want to -map some other /dev/tty* to the real framebuffer device. You might also choose -to compile fbcon as a kernel module or place it permanently in the kernel. - -I do not know of any way to unload fbcon, and fbcon will prevent the -framebuffer device loaded first from unloading. [If there is a way, then -please add a description here!] - -Module unloading, the vesafb method -=================================== - -Configure the kernel: - - <*> Support for frame buffer devices - [*] VESA VGA graphics support - Cyberblade/i1 support - -Add e.g. "video=vesafb:ypan vga=0x307" to the kernel parameters. The ypan -parameter is important, choose any vga parameter you like as long as it is -a graphics mode. - -After booting, load cyblafb without any mode and bpp parameter and assign -cyblafb to individual ttys using con2fb, e.g.: - - modprobe cyblafb - con2fb /dev/fb1 /dev/tty1 - -Unloading cyblafb works without problems after you assign vesafb to all -ttys again, e.g.: - - con2fb /dev/fb0 /dev/tty1 - rmmod cyblafb diff --git a/Documentation/fb/cyblafb/whatsnew b/Documentation/fb/cyblafb/whatsnew deleted file mode 100644 index 76c07a26e044..000000000000 --- a/Documentation/fb/cyblafb/whatsnew +++ /dev/null @@ -1,29 +0,0 @@ -0.62 -==== - - - the vesafb parameter has been removed as I decided to allow the - feature without any special parameter. - - - Cyblafb does not use the vga style of panning any longer, now the - "right view" register in the graphics engine IO space is used. Without - that change it was impossible to use all available memory, and without - access to all available memory it is impossible to ywrap. - - - The imageblit function now uses hardware acceleration for all font - widths. Hardware blitting across pixel column 2048 is broken in the - cyberblade/i1 graphics core, but we work around that hardware bug. - - - modes with vxres != xres are supported now. - - - ywrap scrolling is supported now and the default. This is a big - performance gain. - - - default video modes use vyres > yres and vxres > xres to allow - almost optimal scrolling speed for normal and rotated screens - - - some features mainly usefull for debugging the upper layers of the - framebuffer system have been added, have a look at the code - - - fixed: Oops after unloading cyblafb when reading /proc/io* - - - we work around some bugs of the higher framebuffer layers. diff --git a/Documentation/fb/cyblafb/whycyblafb b/Documentation/fb/cyblafb/whycyblafb deleted file mode 100644 index a123bc11e698..000000000000 --- a/Documentation/fb/cyblafb/whycyblafb +++ /dev/null @@ -1,85 +0,0 @@ -I tried the following framebuffer drivers: - - - TRIDENTFB is full of bugs. Acceleration is broken for Blade3D - graphics cores like the cyberblade/i1. It claims to support a great - number of devices, but documentation for most of these devices is - unfortunately not available. There is _no_ reason to use tridentfb - for cyberblade/i1 + CRT users. VESAFB is faster, and the one - advantage, mode switching, is broken in tridentfb. - - - VESAFB is used by many distributions as a standard. Vesafb does - not support mode switching. VESAFB is a bit faster than the working - configurations of TRIDENTFB, but it is still too slow, even if you - use ypan. - - - EPIAFB (you'll find it on sourceforge) supports the Cyberblade/i1 - graphics core, but it still has serious bugs and developement seems - to have stopped. This is the one driver with TV-out support. If you - do need this feature, try epiafb. - -None of these drivers was a real option for me. - -I believe that is unreasonable to change code that announces to support 20 -devices if I only have more or less sufficient documentation for exactly one -of these. The risk of breaking device foo while fixing device bar is too high. - -So I decided to start CyBlaFB as a stripped down tridentfb. - -All code specific to other Trident chips has been removed. After that there -were a lot of cosmetic changes to increase the readability of the code. All -register names were changed to those mnemonics used in the datasheet. Function -and macro names were changed if they hindered easy understanding of the code. - -After that I debugged the code and implemented some new features. I'll try to -give a little summary of the main changes: - - - calculation of vertical and horizontal timings was fixed - - - video signal quality has been improved dramatically - - - acceleration: - - - fillrect and copyarea were fixed and reenabled - - - color expanding imageblit was newly implemented, color - imageblit (only used to draw the penguine) still uses the - generic code. - - - init of the acceleration engine was improved and moved to a - place where it really works ... - - - sync function has a timeout now and tries to reset and - reinit the accel engine if necessary - - - fewer slow copyarea calls when doing ypan scrolling by using - undocumented bit d21 of screen start address stored in - CR2B[5]. BIOS does use it also, so this should be safe. - - - cyblafb rejects any attempt to set modes that would cause vclk - values above reasonable 230 MHz. 32bit modes use a clock - multiplicator of 2, so fbset does show the correct values for - pixclock but not for vclk in this case. The fbset limit is 115 MHz - for 32 bpp modes. - - - cyblafb rejects modes known to be broken or unimplemented (all - interlaced modes, all doublescan modes for now) - - - cyblafb now works independant of the video mode in effect at startup - time (tridentfb does not init all needed registers to reasonable - values) - - - switching between video modes does work reliably now - - - the first video mode now is the one selected on startup using the - vga=???? mechanism or any of - - 640x480, 800x600, 1024x768, 1280x1024 - - 8, 16, 24 or 32 bpp - - refresh between 50 Hz and 85 Hz, 1 Hz steps (1280x1024-32 - is limited to 63Hz) - - - pci retry and pci burst mode are settable (try to disable if you - experience latency problems) - - - built as a module cyblafb might be unloaded and reloaded using - the vfb module and con2vt or might be used together with vesafb - diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index c19f6feb4e53..db7a4f42edad 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1597,30 +1597,6 @@ config FB_VT8623 Driver for CastleRock integrated graphics core in the VIA VT8623 [Apollo CLE266] chipset. -config FB_CYBLA - tristate "Cyberblade/i1 support" - depends on FB && PCI && X86_32 && !64BIT - select FB_CFB_IMAGEBLIT - ---help--- - This driver is supposed to support the Trident Cyberblade/i1 - graphics core integrated in the VIA VT8601A North Bridge, - also known as VIA Apollo PLE133. - - Status: - - Developed, tested and working on EPIA 5000 and EPIA 800. - - Does work reliable on all systems with CRT/LCD connected to - normal VGA ports. - - Should work on systems that do use the internal LCD port, but - this is absolutely not tested. - - Character imageblit, copyarea and rectangle fill are hw accelerated, - ypan scrolling is used by default. - - Please do read . - - To compile this driver as a module, choose M here: the - module will be called cyblafb. - config FB_TRIDENT tristate "Trident support" depends on FB && PCI @@ -1633,8 +1609,8 @@ config FB_TRIDENT and Blade XP. There are also integrated versions of these chips called CyberXXXX, CyberImage or CyberBlade. These chips are mostly found in laptops - but also on some motherboards. For more information, read - + but also on some motherboards including early VIA EPIA motherboards. + For more information, read Say Y if you have such a graphics board. diff --git a/drivers/video/cyblafb.c b/drivers/video/cyblafb.c deleted file mode 100644 index 9704b73135f5..000000000000 --- a/drivers/video/cyblafb.c +++ /dev/null @@ -1,1683 +0,0 @@ -/* - * Frame buffer driver for Trident Cyberblade/i1 graphics core - * - * Copyright 2005 Knut Petersen - * - * CREDITS: - * tridentfb.c by Jani Monoses - * see files above for further credits - * - */ - -#define CYBLAFB_DEBUG 0 -#define CYBLAFB_KD_GRAPHICS_QUIRK 1 - -#define CYBLAFB_PIXMAPSIZE 8192 - -#include -#include -#include -#include -#include -#include -#include